728x90
반응형
<참고자료> Spring Security 4.1.1 doc |
5.2 httpSecurity
WebSecurityConfig는 사용자 인증에 대한 정보를 포함하고 있다. 어떻게 Spring Security는 사용자가 인증을 하기 위해 필요로 하는 것을 알 수 있을 까? 어떻게 Spring Security는 폼기나 인증을 알 수 있을까? 그 이유는 WebSecurityconfigurerAdapter가 제공하는 configure(HttpSecurity http)안에 있는 아래와 같은 기본 설정을 제공하기 때문이다.
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
위 기본 설정의 의미
- 사용자 인증이 된 요청에 대해서만 요청을 허용한다.
- 사용자는 폼기반 로그인으로 인증 할 수 있습니다.
- 사용자는 HTTP기반 인증으로 인증 할 수 있습니다.
XML설정방법
<http>
<intercept-url pattern="/**" access="authenticated"/>
<form-login />
<http-basic />
<intercept-url pattern="/**" access="authenticated"/>
<form-login />
<http-basic />
</http>
자바코드 설정의 “and()”메소드와 XML tag를 닫는 표현식(“/>”)을 사용하여 우리는 계속 설정할 수 있습니다.
당신이 코드를 읽고, 쓸 수 있는 능력이 있다면, 인증된 요청을 설정하고, 양식 로그인을 설정하고, HTTP 기본 인증을 설정 할 수 있습니다.
그러나, 자바 설정은 기본 URL와 파라미더 설정에 차이가 있다. 사용자 정의 로그인페이지를 만들게 유의하십시오. 그 결과는 우리의 URL이 안전합니다. 또한 확실하진 않지만 Spring Security를 사용함으로 써 우리의 정보 유출을 방지하는데 도움이 됩니다.
5.3 Java Configuration and Form Login
우리는 어떠한 HTML파일이나 JSP를 언급하지 않았기 때문에 로그인 양식이 로그인하라는 메시지가 표시될때 당신은 궁금해 할 것입니다.
Spring Security의 기본 설정에는 로그인페이지의 URL을 명시하지 않았기 때문에 Spring Security는 보내진 로그인을 처리하는 활성화되고 표준 값(standard values)을 사용하는 URL의 특성에 따라 자동으로 하나를 생성하고 기본 대상 URL은 사용자가 로그인한 후에 보낼 것입니다.
자동으로 생성된 로그페이지는 빠르게 생성되어 얻기 편리하지만 대부분의 어플리케이션은 자신만의 로그인페이지를 제공할 것입니다. 아래의 설정은 우리가 우리만의 로그인페이지를 만들 수 있도록 도와줍니다.
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
우리가 현재 설정으로 JSP로 구현한 로그인 페이지는 아래와 같다.
<c:url value="/login" var="loginUrl"/>
<form action="${loginUrl}" method="post">
<c:if test="${param.error != null}">
<p>
Invalid username and password.
</p>
</c:if>
<c:if test="${param.logout != null}">
<p>
You have been logged out.
</p>
</c:if>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username"/>
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
</p>
<input type="hidden"
name="${_csrf.parameterName}"
value="${_csrf.token}"/>
<button type="submit" class="btn">Log in</button>
<form action="${loginUrl}" method="post">
<c:if test="${param.error != null}">
<p>
Invalid username and password.
</p>
</c:if>
<c:if test="${param.logout != null}">
<p>
You have been logged out.
</p>
</c:if>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username"/>
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
</p>
<input type="hidden"
name="${_csrf.parameterName}"
value="${_csrf.token}"/>
<button type="submit" class="btn">Log in</button>
</form>
5.4 Authorise Requests
이 예제는 인증할 사용자와 어플리케이션의 모든 URL에 대해 요청합니다. 우리는 http.authorizeRequests()메소드를 통해 여러 자식을 추가하여 URL에 대한 요구사항을 정의할 수 있습니다.
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
// ...
.formLogin();
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
// ...
.formLogin();
}
5.5 Hadling Logouts
- HTTP Session 무효화
- 본인의 설정된 인증을 깨끗히 제거
-
SecurityContextHolder
를 초기화 -
/login?logout
로 이동
로그인 기능과 설정이 유사하지만 당신의 로그아웃 요구사항에 맞추기 위해 다양한 옵션이 있습니다.
protected void configure(HttpSecurity http) throws Exception {
http
.logout()
.logoutUrl("/my/logout")
.logoutSuccessUrl("/my/index")
.logoutSuccessHandler(logoutSuccessHandler)
.invalidateHttpSession(true)
.addLogoutHandler(logoutHandler)
.deleteCookies(cookieNamesToClear)
.and()
...
http
.logout()
.logoutUrl("/my/logout")
.logoutSuccessUrl("/my/index")
.logoutSuccessHandler(logoutSuccessHandler)
.invalidateHttpSession(true)
.addLogoutHandler(logoutHandler)
.deleteCookies(cookieNamesToClear)
.and()
...
}
HttpSession
무효화를 할지 여부를 지정한다. 이 옵션의 기본은 true입니다. SecurityContextLogoutHandler기반 아래 설정합니다. 자세한 내용은 JavaDoc참고바람.일반적으로 로그아웃기능 순서에 따라 정의합니다. 당신은
LogoutHandler
또는 LogoutSuccessHandler
를 구현할 수 있습니다. 많은 일반적인 시나리오대로라면 이 핸들러는 fluent API를 사용할 때 지원합니다. 5.5.1 LogoutHandler
일반적으로,
LogoutHandler
구현은 로그아웃 처리에 참여할 수 있는 클래스를 나타냅니다. 이들은 필요한 정리를 수행하기 위해 호출되어지는 것으로 예상됩니다. 따라서 이것은 예외를 발생하지 않아야합니다. 다양한 구현 방법이 제공됩니다.- PersistentTokenBasedRememberMeServices
- TokenBasedRememberMeServices
- CookieClearingLogoutHandler
- CsrfLogoutHandler
- SecurityContextLogoutHandler
Section 17.4, “Remember-Me Interfaces and Implementations”에 자세하게 나와 있습니다.
직접
LogoutHandler
구현을 제공하는 대신 fluent API는 각각의 LogoutHandler
구현들을 제공하는 shortcuts을 제공합니다. 예를 들면 deleteCookies()
는 로그아웃에 성공시 지우기 위해 하나 이상의 쿠키들을 지우기 위한 이름을 지정하는데 허용합니다. 이 shortcut은 추가된 CookieClearingLogoutHandler
와 비교합니다. 5.5.2 LogoutSuccessHandler
LogoutSuccessHandler
는 예를 들어 처리하려는 LogoutFilter
에 의해 성공적으로 로그아웃된 후에 호출됩니다. 해당 목적지로 전달 혹은 리디렉션. 인터페이스가 LogoutHandler
와 거의 동일하지만 예외를 일으킬 수 있습니다.다음과 같이 implementations가 제공됩니다.
- SimpleUrlLogoutSuccessHandler
- HttpStatusReturningLogoutSuccessHandler
위에 언급된 바와 같이 당신은
SimpleUrlLogoutSuccessHandler
를 직접 정의 할 필요가 없습니다. 대신 fluent API는logoutSuccessUrl()
을 정의하기 위한 shutcut을 제공합니다. 이것은 SimpleUrlLogoutSuccessHandler
내부에서 세팅되어집니다. 로그아웃 후에 제공된 URL로 리디렉션되어 집니다. 기본값은 /login?logout
입니다.HttpStatusReturningLogoutSuccessHandler
은 REST API 유형의 시나리오에서 흥미를 유발할 수 있습니다. 로그아웃 성공시 URL로 이동하는 대신 LogoutSuccessHandler
는 반환 할 수 있는 일반 HTTP상태코드를 제공할 수 있습니다. 설정되지 않은 경우 상태코드 200이 기본값으로 리턴 되어 집니다. 5.5.3 Further Logout-Related References
- Logout Handling
- Testing Logout
- HttpServletRequest.logout()
- Section 17.4, “Remember-Me Interfaces and Implementations”
- Logging Out in section CSRF Caveats
- Section Single Logout (CAS protocol)
- Documentation for the logout element in the Spring Security XML Namespace section
5.6 Authentication
지금까지 우리는 가장 기본적인 인증 설정을 살펴보았습니다. 인증 설정을 위한 조금 더 고급 옵션에 대해서 살펴보겠습니다.
5.6.1 In Memory Authentication
우리는 단일 사용자를 위한 메모리 인증에 대해 이미 보았습니다. 아래는 다중 사용자들을 위한 설정 예제입니다.
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
5.6.2 JDBC Authentication
우리는 JDBC기반 인증을 도와주는 updates를 찾을 수 있습니다. 아래의 예는 당신의 어플리케이션안에 이미
DataSource
를 정의했다고 가정합니다. jdbc-javaconfig샘플은 완벽하게 JDBC기반 인증을 사용하는 예제를 지원합니다.
@Autowired
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
5.6.3 LDAP Authentication
당신은 LDAP기반 인증을 도와주는 updates를 찾을 수 있습니다. ldap-jávaçonfig샘플은 완벽하게 LDAP기반 인증을 사용하는 예제를 지원합니다.
@Autowired
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups");
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups");
}
위 예제는 보시는바와 같이 LDIF와 embedded Apache DS LDAP instance를 사용합니다.
users.ldif
dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rod Johnson
sn: Johnson
uid: admin
userPassword: password
dn: uid=user,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dianne Emu
sn: Emu
uid: user
userPassword: password
dn: cn=user,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: user
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
uniqueMember: uid=user,ou=people,dc=springframework,dc=org
dn: cn=admin,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: admin
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rod Johnson
sn: Johnson
uid: admin
userPassword: password
dn: uid=user,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dianne Emu
sn: Emu
uid: user
userPassword: password
dn: cn=user,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: user
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
uniqueMember: uid=user,ou=people,dc=springframework,dc=org
dn: cn=admin,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: admin
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
5.6.4 AuthenticationProvider
당신은 빈으로 정의한
AuthenticationProvider
을 보여줌으로써 인증을 정의 할 수 있습니다. 예를 들어 가령 SpringAuthenticationProvider
은 AuthenticationProvider
을 구현한다고 가정하고 인증을 정의합니다.@Bean
public SpringAuthenticationProvider springAuthenticationProvider() {
return new SpringAuthenticationProvider();
}
public SpringAuthenticationProvider springAuthenticationProvider() {
return new SpringAuthenticationProvider();
}
5.6.5 UserDetailsService
당신으로 빈으로 정의한
UserDetailsService
을 보여줌으로써 인증을 정의할 수 있습니다. 예를 들어 가령SpringDataUserDetailsService
는 UserDetailsService
를 구현한다고 가정하고 인증을 정의합니다. @Bean
public SpringDataUserDetailsService springDataUserDetailsService() {
return new SpringDataUserDetailsService();
public SpringDataUserDetailsService springDataUserDetailsService() {
return new SpringDataUserDetailsService();
}
당신은 빈으로 정의한
PasswordEncoder
를 사용하여 어떻게 비밀번호를 인코딩할지 정의할 수 있습니다. 예를 들어 당신이 bcrypt를 사용한다면 당신은 아래와 같은 빈을 정의하여 추가할 수 있습니다.@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
5.6.6 LDAP Authentication
5.7 Multiple HttpSecurity
우리는 여러 <http> 블록을 쓸 수 있었던 것처럼 HttpSecurity instance를 다중으로 설정 할 수 있습니다. 여기서 핵심은
WebSecurityConfigurationAdapter
를 여러 아이템으로 확장하는 것입니다. 예를 들어 시작URL이 /api/
인 서로 다른 설정을 가진 예제입니다.@EnableWebSecurity
public class MultiHttpSecurityConfig {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
@Configuration
@Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
@Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
public class MultiHttpSecurityConfig {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
@Configuration
@Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
@Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}
WebSecurityConfigurerAdapter
를 먼저 고려해야 함을 명시학 위해 @Order
를 포함하고 있는 WebSecurityConfigurerAdapter
의 instance를 생성한다. WebSecurityConfigurerAdapter
의 다른 instance 생성합니다. URL이 시작이 /api/
가 아니라면 이 설정이 사용되어 집니다. 이 설정은 @Order
값 1을 갖기 때문에 ApiWebSecurityConfigurationAdapter
후에 간주됩니다. 5.8 Method Security
Spring Security 2.0 이후부터 당신의 layer method의 추가적인 보안을 위한 상당히 향상된 기능을 가지고 있습니다. 이 프레임워크는 원래
@Secured
어노테이션뿐만아니라 JSP-250 보안 어노테이션에 대한 지원을 제공합니다. 3.0 버전부터는 새로운 expression-based annotations(표현기반 어노테이션)을 만들 수 있습니다. 당신은 빈선언으로 만들어진 intercept-methods
element를 사용하여 하나의 반에 보안을 적용하거나, 또는 AspectJ 스타일의 포인컷을 사용하여 전체 서비스 계층에서 여러 빈을 보호할 수 있습니다.5.8.1 EnableGolbalMethodSecurity
우리는
@Configuration
하에 @EnableGlobalMethodSecurity
어노테이션을 사용하여 어노테이션 기반 보안을 적용할 수 있습니다. 예를 들어 Spring Security @Secured
을 적용한 예제입니다.EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
public class MethodSecurityConfig {
// ...
}
(클래스나 인터스페이스) 메소드를 위해 추가한 어노테이션은 메소드의 접근을 제한 할 수 있습니다. Spring Security native 어노테이션은 메소드를 위한 attributes를 셋팅하여 정의됩니다. 다음은 실제로 결정 할 AccessDecisionManager가 전달 될 것입니다.
public interface BankService {
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account[] findAccounts();
@Secured("ROLE_TELLER")
public Account post(Account account, double amount);
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account[] findAccounts();
@Secured("ROLE_TELLER")
public Account post(Account account, double amount);
}
JSR-250 어노테이션을 위한 지원을 사용할 수 있습니다.
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class MethodSecurityConfig {
// ...
public class MethodSecurityConfig {
// ...
}
다음은 표준 기반이며 기본 권한 기반 제약조건을 적용할 수 있지만 강력한 Spring Security native 어노테이션을 가질 수 없습니다. 새로운 표현 기반 표현식을 사용하기 위해서 당신은 사용할 수 있습니다.
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
// ...
public class MethodSecurityConfig {
// ...
}
그리고 자바코드와 동등합니다.
public interface BankService {
@PreAuthorize("isAnonymous()")
public Account readAccount(Long id);
@PreAuthorize("isAnonymous()")
public Account[] findAccounts();
@PreAuthorize("hasAuthority('ROLE_TELLER')")
public Account post(Account account, double amount);
}
@PreAuthorize("isAnonymous()")
public Account readAccount(Long id);
@PreAuthorize("isAnonymous()")
public Account[] findAccounts();
@PreAuthorize("hasAuthority('ROLE_TELLER')")
public Account post(Account account, double amount);
}
5.8.2 GlobalMethodSecurityConfiguration
때때로 당신은
@EnableGlobalMethodSecurity
어노테이션 허용을 가능하게 하여 보다 더 복잡한 작업을 수행할 수 있습니다. 이러한 경우 예를 들어 당신은 당신의 서브클래스에 존재함을 보장하는 @EnableGlobalMethodSecurity
어노테이션을 GlobalMethodSecurityConfiguration
에 확장할 수 있습니다. 예를 들어 당신이 MethodSecurityExpressionHandler
정의를 제공하기를 원한다면 당신은 아래 처럼 설정해야 합니다.@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
// ... create and return custom MethodSecurityExpressionHandler ...
return expressionHandler;
}
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
// ... create and return custom MethodSecurityExpressionHandler ...
return expressionHandler;
}
}
메소드에 대한 추가적인 정보는
GlobalMethodSecurityConfiguration
Javadoc을 참고하세요728x90
반응형