스프링 시큐리티
스프링 시큐리티는 필터체인으로 인증과 권한을 확인한다.
따라서 webApplicationContext에 설정을 할 경우 모든 bean이 생성되지 않은 상태에서 로딩이 되므로 에러가 발생한다. rootApplicationContext에 설정하여 우선적으로 bean이 만들어질 수 있도록 해야 한다.
설정방법은 다음과 같다.
파일명 : pom.xml
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.1.3.RELEASE<<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>4.2.0.RELEASE</version>
</dependency>/version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
파일명 : web.xml
<!-- spring security filter -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
config/app/SecurityConfig.java 생성
package com.cafe24.config.app;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.filter.DelegatingFilterProxy;
/*
Security Filter Chain
1. ChannelProcessingFilter
2. SecurityContextPersistenceFilter ( auto-config default ) 필수
3. ConcurrentSessionFilter
4. LogoutFilter ( auto-config default ) 필수
5. UsernamePasswordAuthenticationFilter ( auto-config default ) 필수
6. DefaultLoginPageGeneratingFilter ( auto-config default )
7. CasAuthenticationFilter
8. BasicAuthenticationFilter ( auto-config default ) 필수
9. RequestCacheAwareFilter ( auto-config default )
10. SecurityContextHolderAwareRequestFilter ( auto-config default )
11. JaasApiIntegrationFilter
12. RememberMeAuthenticationFilter
13. AnonymousAuthenticationFilter ( auto-config default )
14. SessionManagementFilter ( auto-config default )
15. ExceptionTranslationFilter ( auto-config default ) 필수
16. FilterSecurityInterceptor ( auto-config default ) 필수
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// Spring security filter connection
// WebSecurity object create DelegatingFilterProxy Bean object named of springSecurityFilterChain
// DelegatingFilterProxy Bean delegate to the many Spring Security filters
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
// 예외 웹접근 url 설정한다. configure exception web approach
// ACL(접근제외에서 예외 URL를 설정
}
// @Bean(name = "springSecurityFilterChain")
// public void FilterChainProxy() {
// }
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
super.configure(http);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
super.configure(auth);
}
}
{domain}.config 에서 SecurityConfig.class import 작업
package com.cafe24.mysite.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import com.cafe24.config.app.DBConfig;
import com.cafe24.config.app.MyBatisConfig;
import com.cafe24.config.app.SecurityConfig;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan({"com.cafe24.mysite.service", "com.cafe24.mysite.repository", "com.cafe24.mysite.aspect"})
@Import({DBConfig.class, MyBatisConfig.class, SecurityConfig.class})
public class AppConfig {
}
시큐리티가 동작하는 원리는 다음과 같다.
- 유저가 로그인을 시도 (http request)
- AuthenticationFilter 에서부터 위와같이 user DB까지 타고 들어감
- DB에 있는 유저라면 UserDetails 로 꺼내서 유저의 session 생성
- spring security의 인메모리 세션저장소인 SecurityContextHolder 에 저장
- 유저에게 session ID와 함께 응답을 내려줌
- 이후 요청에서는 요청쿠키에서 JSESSIONID를 까봐서 검증 후 유효하면 Authentication를 쥐어준다
접근 제어를 컨트롤하여 URL에 따라 Security 필터에 걸리지 않도록 예외 설정하기
파일명 : SecurityConfig.java
// Spring security filter connection
// WebSecurity object create DelegatingFilterProxy Bean object named of springSecurityFilterChain
// DelegatingFilterProxy Bean delegate to the many Spring Security filters
@Override
public void configure(WebSecurity web) throws Exception {
// super.configure(web);
// 예외 웹접근 url 설정한다. configure exception web approach
// ACL(Access Control List)에서 예외 URL를 설정
web.ignoring().antMatchers("/assets/**");
web.ignoring().antMatchers("/favicon.ico");
// web.ignoring().regexMatchers("\\A/assets/.*\\Z");
// web.ignoring().regexMatchers("\\^/favicon.ico\\Z");
}
URL에 따라 권한이 있는 지 확인(보안)
// Interceptor URL의 요청을 안전하게 보호(보안)하는 방법을 설정(ACL 작성)
/*
deny all
/user/update -> (ROLE_USER, ROLE_ADMIN) -> Authenticated
/user/logout -> (ROLE_USER, ROLE_ADMIN) -> Authenticated
/board/write -> (ROLE_USER, ROLE_ADMIN) -> Authenticated
/board/delete -> (ROLE_USER, ROLE_ADMIN) -> Authenticated
/board/modify -> (ROLE_USER, ROLE_ADMIN) -> Authenticated
/admin/** -> ROLE_ADMIN(Authorized)
allow all
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//
// 1. ACL 설정
//
http
.authorizeRequests()
// 인증이 되었있을 때(authenticated?)
.antMatchers("/user/update", "/user/logout").authenticated()
.antMatchers("/board/write", "/board/delete", "/board/modify").authenticated()
// ADMIN Authority(ADMIN 권한, ROLE_ADMIN)
//.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
//.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
// 모두 허용
//.antMatchers("/**").permitAll()
.anyRequest().permitAll();
// Temporary for Testing
http.csrf().disable();
//
// 2. 로그인 설정
//
http
.formLogin()
.loginPage("/user/login")
.loginProcessingUrl("/user/auth")
.failureUrl("/user/login?result=fail")
.defaultSuccessUrl("/", true)
.usernameParameter("email")
.passwordParameter("password");
//
// 3. 로그아웃 설정
//
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/user/logout"))
.logoutSuccessUrl("/")
.invalidateHttpSession(true);
}
LOGIN 페이지에서 정보를 보내기 (UserDetailService 구현)
파일명 : {domain}/security/SecurityUser.java
package com.cafe24.mysite.security;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class SecurityUser implements UserDetails {
private static final long serialVersionUID = 1L;
private Collection<? extends GrantedAuthority> authorities;
private String username; // principal(biz name: email)
private String password; // credential
// etc
private String name; // biz data
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
파일명 : {domain}/security/UserDetailsServiceImpl.java
package com.cafe24.mysite.security;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import com.cafe24.mysite.repository.UserDao;
import com.cafe24.mysite.vo.UserVo;
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserVo userVo = userDao.get(username);
SecurityUser securityUser = new SecurityUser();
if( userVo != null ) {
// mock data
//String role = userVo.getRole();
String role = "ROLE_USER";
securityUser.setName(userVo.getName()); // biz data
securityUser.setUsername(userVo.getEmail()); // principal
securityUser.setPassword(userVo.getPassword()); // credential
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(role));
securityUser.setAuthorities(authorities);
}
return securityUser;
}
}
파일명 : {domain}/config.app/SecurityConfig.java
// UserDetailsService를 설정
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
UserDetail(Interface)로 구현된 loadUserByUsername(String name)을 통해서 UserDetail에 String name을 주입할 수 있게 되었다. UserDetailsSerivce를 위한 Interface 구현체를 만들고 최종적으로 UserDetail에 String name및 UserDetail의 내용이 오버라이드 되어 되돌려질 수 있다면 우리는 Security에서 UserDetail에 대한 정보를 컨트롤 할 수 있으며 동일한 인터페이스로 구현된 Spring security의 타 기능들을 사용할 수 있게 되는 것이다.
그 정보를 설정해주기 위하여 AuthenticationManagerBuilder에 해당하는 auth에 Bean정보를 주입하여 전달한다.
'JAVA > Spring Framework' 카테고리의 다른 글
[Spring] 다중 데이터소스 설정(Multiple Datasource JPA, Mybatis) (0) | 2020.01.07 |
---|---|
GSON 과 JSON 차이 및 변형 (0) | 2019.07.22 |
[Spring] Logging (0) | 2019.05.21 |
[Spring] Message converter (0) | 2019.05.21 |
[Spring] 스프링 설정 순서 (0) | 2019.05.19 |