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기반 인증으로 인증 할 수 있습니다.
XML설정방법
<http>
        <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"1
                        .permitAll();        
2
}
1  로그인 페이지의 위치를 명시합니다.
2  든 사용자들에게 접속을 허용하는 로그인페이지 “formLogin().permmitAll()” 메소드는 폼 기반 로그인과 관련된 모든 URL에 대한 모든 사용자의 접근 구너한을 허용합니다. 

우리가 현재 설정으로 JSP로 구현한 로그인 페이지는 아래와 같다.
<c:url value="/login" var="loginUrl"/>
<form action="${loginUrl}" method="post">       1
        <c:if test="${param.error != null}">        
2
                <p>
                        Invalid username and password.
                </p>
        </c:if>
        <c:if test="${param.logout != null}">       
3
                <p>
                        You have been logged out.
                </p>
        </c:if>
        <p>
                <label for="username">Username</label>
                <input type="text" id="username" name="username"/>       
4
        </p>
        <p>
                <label for="password">Password</label>
                <input type="password" id="password" name="password"/>   
5
        </p>
        <input type="hidden"                        
6
                name="${_csrf.parameterName}"
                value="${_csrf.token}"/>
        <button type="submit" class="btn">Log in</button>
</form>
1 POST형식의 /login URL은 사용자 인증을 시도합니다.
2 쿼리 파라미터에 에러가 존재한다면 인증이 실패합니다.
3 쿼리 파라미터에 로그아웃이 존재한다면 사용자는 성공적으로 로그아웃한 것입니다.
4 ‘username’은 HTTP 파라미터에서 ‘username’이라는 이름으로 존재해야 합니다.
5 ‘password’는 HTTP파라미더에서 ‘password’라는 이름으로 존재해야 합니다.

6 CSRF Token에 대한 레퍼런스 문서를 읽어야 합니다.(We must Section 18.4.3, “Include the CSRF Token” To learn more read the Chapter 18, Cross Site Request Forgery (CSRF) section of the reference)


5.4 Authorise Requests

이 예제는 인증할 사용자와 어플리케이션의 모든 URL에 대해 요청합니다. 우리는 http.authorizeRequests()메소드를 통해 여러 자식을 추가하여 URL에 대한 요구사항을 정의할 수 있습니다.
protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()                                                                1
                        .antMatchers("/resources/**""/signup""/about").permitAll()                  
2
                        .antMatchers("/admin/**").hasRole("ADMIN")                                      
3
                        .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")            
4
                        .anyRequest().authenticated()                                                   
5
                        .and()
                // ...
                .formLogin();
}
1  http.authorizeRequests()메소드를 각각의 matcher가 선언된 순서대로 간주 되는 여러 자식이 있습니다.
2 우리는 모든 사용자가 접근할 수 있는 여러개의 URL패턴을 지정했습니다. URL이 “/resources”로 시작하고, “/signup” 혹은 “/about”일 경우 사용자는 해당 요청에 접근할 수 있습니다.
3 “/admin/”로 시작하는 URL은 권한이 “ROLE_ADMIN”인 사용자에게 제한됩니다. 당신은 hasRole 메소드를 호출하기 때문에 우리는 “ROLE_” 접두사를 지정하지 않아도 알 수 있습니다.
4 “/db/“로 시작한 요청은 “ROLE_ADMIN”과 “ROLE_DBA”을 가지고 있는 사용자입니다. 당신은 hasRole 메소드를 사용하고 있기 때문에 우리는 “ROLE_” 접두사를 지정하지 않아도 알 수 있습니다.

5 일치 하지 않은 URL은 사용자 인증을 요구합니다.


5.5 Hadling Logouts

WebSecurityConfigurerAdapter를 사용할 때 로그아웃 기능들은 자동으로 제공됩니다.  기본값은 URL /logout 으로 접근하여 사용자 로그아웃을 합니다.
  • HTTP Session 무효화
  • 본인의 설정된 인증을 깨끗히 제거
  •  SecurityContextHolder를 초기화
  •  /login?logout로 이동
로그인 기능과 설정이 유사하지만 당신의 로그아웃 요구사항에 맞추기 위해 다양한 옵션이 있습니다.
protected void configure(HttpSecurity http) throws Exception {
        http
                .logout()                                                               1
                        .logoutUrl("/my/logout")                                                 
2
                        .logoutSuccessUrl("/my/index")                                           
3
                        .logoutSuccessHandler(logoutSuccessHandler)                              
4
                        .invalidateHttpSession(true)                                             
5
                        .addLogoutHandler(logoutHandler)                                         
6
                        .deleteCookies(cookieNamesToClear)                                       
7
                        .and()
                ...
}
1 로그아웃을 지원합니다. WebSecurityConfigurerAdapter를 사용할 때 자동으로 지원됩니다.
2 로그아웃시 발생하는 URL입니다.(기본값은/logout) CSRF보호가 활성화 되어있다면 요청은 POST형식이어야 합니다. 자세한 내용은  JavaDoc참고바람.
3 URL은 로그아웃이 발생한 이후로 이동합니다. 기본값은/login?logout입니다. 자세한 내용은  JavaDoc참고바람.
4 사용자 지정 LogoutSuccessHandler를 지정할 수 있습니다. 지정시 logoutSuccessUrl()은 무시됩니다. 자세한 내용은 JavaDoc참고바람.
5 로그아웃 시HttpSession무효화를 할지 여부를 지정한다. 이 옵션의 기본은 true입니다. SecurityContextLogoutHandler기반 아래 설정합니다. 자세한 내용은  JavaDoc참고바람.
6 LogoutHandler추가합니다. SecurityContextLogoutHandler 는 기본적으로 과거의 LogoutHandler를 추가합니다.
7 로그아웃 성공시 제거할 쿠키의 이름을 지정합니다. 이것은 CookieClearingLogoutHandler을 추가하기 위한 지름길입니다.

일반적으로 로그아웃기능 순서에 따라 정의합니다. 당신은  LogoutHandler 또는  LogoutSuccessHandler 를 구현할 수 있습니다. 많은 일반적인 시나리오대로라면 이 핸들러는 fluent API를 사용할 때 지원합니다.  

5.5.1 LogoutHandler
일반적으로,  LogoutHandler 구현은 로그아웃 처리에 참여할 수 있는 클래스를 나타냅니다. 이들은 필요한 정리를 수행하기 위해 호출되어지는 것으로 예상됩니다. 따라서 이것은 예외를 발생하지 않아야합니다. 다양한 구현 방법이 제공됩니다.
직접  LogoutHandler 구현을 제공하는 대신 fluent API는 각각의  LogoutHandler 구현들을 제공하는 shortcuts을 제공합니다. 예를 들면  deleteCookies() 는 로그아웃에 성공시 지우기 위해 하나 이상의 쿠키들을 지우기 위한 이름을 지정하는데 허용합니다. 이 shortcut은 추가된  CookieClearingLogoutHandler와 비교합니다.           

5.5.2 LogoutSuccessHandler
 LogoutSuccessHandler 는 예를 들어 처리하려는 LogoutFilter에 의해 성공적으로 로그아웃된 후에 호출됩니다. 해당 목적지로 전달 혹은 리디렉션. 인터페이스가  LogoutHandler 와 거의 동일하지만 예외를 일으킬 수 있습니다.

다음과 같이 implementations가 제공됩니다.


위에 언급된 바와 같이 당신은  SimpleUrlLogoutSuccessHandler 를 직접 정의 할 필요가 없습니다. 대신 fluent API는logoutSuccessUrl()  을 정의하기 위한  shutcut을 제공합니다. 이것은  SimpleUrlLogoutSuccessHandler 내부에서 세팅되어집니다. 로그아웃 후에 제공된 URL로 리디렉션되어 집니다. 기본값은  /login?logout입니다.

HttpStatusReturningLogoutSuccessHandler 은 REST API 유형의 시나리오에서 흥미를 유발할 수 있습니다. 로그아웃 성공시 URL로 이동하는 대신 LogoutSuccessHandler 는 반환 할 수 있는 일반 HTTP상태코드를 제공할 수 있습니다. 설정되지 않은 경우 상태코드 200이 기본값으로 리턴 되어 집니다.    

5.5.3 Further Logout-Related References

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");
}

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");
}

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");
}
위 예제는 보시는바와 같이 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

5.6.4 AuthenticationProvider
당신은 빈으로 정의한  AuthenticationProvider 을 보여줌으로써 인증을 정의 할 수 있습니다.  예를 들어 가령  SpringAuthenticationProvider 은  AuthenticationProvider 을 구현한다고 가정하고 인증을 정의합니다.
@Bean
public SpringAuthenticationProvider springAuthenticationProvider() {
        return new SpringAuthenticationProvider();
}

5.6.5 UserDetailsService
당신으로 빈으로 정의한  UserDetailsService 을 보여줌으로써 인증을 정의할 수 있습니다. 예를 들어 가령SpringDataUserDetailsService 는  UserDetailsService 를 구현한다고 가정하고 인증을 정의합니다.         
@Bean
public SpringDataUserDetailsService springDataUserDetailsService() {
        return new SpringDataUserDetailsService();
}
당신은 빈으로 정의한   PasswordEncoder 를  사용하여 어떻게 비밀번호를 인코딩할지 정의할 수 있습니다. 예를 들어 당신이 bcrypt를 사용한다면 당신은 아래와 같은 빈을 정의하여 추가할 수 있습니다.
@Bean
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) { 1
                auth
                        .inMemoryAuthentication()
                                .withUser("user").password("password").roles("USER").and()
                                .withUser("admin").password("password").roles("USER""ADMIN");
        }

        @Configuration
        @Order(1)                                                        
2
       public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
                protected void configure(HttpSecurity http) throws Exception {
                        http
                                .antMatcher("/api/**")                               
3
                                .authorizeRequests()
                                        .anyRequest().hasRole("ADMIN")
                                        .and()
                                .httpBasic();
                }
        }

        @Configuration                                                   
4
       public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

                @Override
                protected void configure(HttpSecurity http) throws Exception {
                        http
                                .authorizeRequests()
                                        .anyRequest().authenticated()
                                        .and()
                                .formLogin();
                }
        }
}
1 기본적인 인증 설정
2   WebSecurityConfigurerAdapter 를 먼저 고려해야 함을 명시학 위해 @Order 를 포함하고 있는 WebSecurityConfigurerAdapter 의 instance를 생성한다.     
3   http.antMatcher 는  HttpSecurity 가  /api/ URL로 적용한 상태를 나타냅니다.

4   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 {
// ...
}
(클래스나 인터스페이스) 메소드를 위해 추가한 어노테이션은 메소드의 접근을 제한 할 수 있습니다. 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);
}
JSR-250 어노테이션을 위한 지원을 사용할 수 있습니다.
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class MethodSecurityConfig {
// ...
}
다음은 표준 기반이며  기본 권한 기반  제약조건을 적용할 수 있지만 강력한 Spring Security native 어노테이션을 가질 수 없습니다. 새로운 표현 기반 표현식을 사용하기 위해서 당신은 사용할 수 있습니다.
@EnableGlobalMethodSecurity(prePostEnabled = true)
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);
}
    
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;
        }
}

메소드에 대한 추가적인 정보는  GlobalMethodSecurityConfiguration Javadoc을 참고하세요


728x90
반응형
블로그 이미지

nineDeveloper

안녕하세요 현직 개발자 입니다 ~ 빠르게 변화하는 세상에 뒤쳐지지 않도록 우리모두 열심히 공부합시다 ~! 개발공부는 넘나 재미있는 것~!

,