Home Form Login
Post
Cancel

Form Login

Login

DefaultLoginPageGeneratingFilter

  • Get /login을 처리하며 별도의 로그인 페이지를 설정하지 않으면 제공되는 필터로 기본 login 폼을 제공한다.

UsernamePasswordAuthenticationFilter

  • POST /login을 처리한다. processingURL을 변경하면 주소를 바꿀 수 있다.
  • form인증을 처리해주는 필터로 스프링 시큐리티에서 가장 일반적으로 사용된다.
  • 주요 설정 정보
    • filterProcessingUrl : 로그인을 처리해 줄 URL (POST)
    • username parameter : POST에 username에 대한 값을 넘겨줄 인자의 이름
    • password parameter : POST에 password에 대한 값을 넘겨줄 인자의 이름
    • 로그인 성공시 처리 방법
      • defaultSuccessUrl : alwaysUse 옵션 설정이 중요

        1
        2
        3
        
          http.formLogin(login->
              login.defaultSuccessUrl("/", alwaysUse = false)
          )
        
        • login이 성공하면 보내줄 page를 작성할 수 있는데 특정 페이지에서 작업 도중 Login이 필요해졌다면 Login성공 이후 원래 있던 페이지로 가야 하므로 alwaysUse는 false로 해주어야 한다.
      • successHandler

    • 로그인 실패시 처리 방법
      • failureUrl
      • failureHandler
    • authenticationDetailSource : Authentication 객체의 details 에 들어갈 정보를 직접 만들어 줌.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
		throws AuthenticationException {
	if (this.postOnly && !request.getMethod().equals("POST")) {
		throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
	}
	String username = obtainUsername(request);
	username = (username != null) ? username : "";
	username = username.trim();
	String password = obtainPassword(request);
	password = (password != null) ? password : "";
	UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
	// Allow subclasses to set the "details" property
	setDetails(request, authRequest);
	return this.getAuthenticationManager().authenticate(authRequest);
}

Logout

DefaultLogoutPageGeneratingFilter

  • GET POST /logout 을 처리하고, DefaultLoginPageGeneratingFilter를 사용하는 경우 같이 제공한다.

LogoutFilter

  • POST /logout 을 처리. processiongUrl 을 변경하면 바꿀 수 있음.
  • session, SecurityContext, csrf, 쿠키, remember-me 쿠키 등을 삭제처리 함.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
      private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
          if (requiresLogout(request, response)) {
              Authentication auth = SecurityContextHolder.getContext().getAuthentication();
              if (this.logger.isDebugEnabled()) {
                  this.logger.debug(LogMessage.format("Logging out [%s]", auth));
              }
              this.handler.logout(request, response, auth);
              this.logoutSuccessHandler.onLogoutSuccess(request, response, auth);
              return;
          }
          chain.doFilter(request, response);
      }
    

Security 적용

root Page만 전체 접근 허용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(request->{
                    request
                            .antMatchers("/").permitAll()
                            .anyRequest().authenticated()
                    ;
                })
        ;
    }
}
  • antMatchers로 “/”접근만 permitAll로 인증 없이 접근 가능하게 하고, 그 이외의 Request에 대해서는 인증을 요구하도록 수정
  • 문제 : WebResource도 인증이 필요하기 때문에 css javascript등을 client가 받지 못한다.
  • 해결 : webResource에 대해서는 ignore처리해 인증이 필요 없도록 해야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(request->{
                    request
                            .antMatchers("/").permitAll()
                            .anyRequest().authenticated()
                    ;
                })
        ;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .requestMatchers(
                        PathRequest.toStaticResources().atCommonLocations()
                );
    }
}
  • resources/static 하위에 css, js를 resource로 설정해 spring security filter를 거치지 않도록 한다.

Login 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests(request->{
            request
                    .antMatchers("/").permitAll()
                    .anyRequest().authenticated();
        }).formLogin(
                login->login.loginPage("/login").permitAll()
        );
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .requestMatchers(
                        PathRequest.toStaticResources().atCommonLocations()
                );
    }
}
  • formLogin을 통해 Login을 추가한다. Login Page를 지정하지 않으면 defaultLoginPage가 생성된다.
  • permitAll을 해주는 이유는 무한 루프를 방지하기 위함이다.

User 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser(
                        User.withDefaultPasswordEncoder()
                                .username("user1")
                                .password("1111")
                                .roles("USER")
                ).withUser(
                        User.withDefaultPasswordEncoder()
                                .username("admin")
                                .password("2222")
                                .roles("ADMIN")
        );

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests(request->{
            request
                    .antMatchers("/").permitAll()
                    .anyRequest().authenticated();
        }).formLogin(
                login->login.loginPage("/login").permitAll()
        );
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .requestMatchers(
                        PathRequest.toStaticResources().atCommonLocations()
                );
    }
}

추가 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser(
                        User.withDefaultPasswordEncoder()
                                .username("user1")
                                .password("1111")
                                .roles("USER")
                ).withUser(
                        User.withDefaultPasswordEncoder()
                                .username("admin")
                                .password("2222")
                                .roles("ADMIN"));
    }

    @Bean
    RoleHierarchy roleHierarchy(){
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER"); // 권한별 계층 설정
        return roleHierarchy;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests(request->{
            request
                    .antMatchers("/").permitAll()
                    .anyRequest().authenticated();
        }).formLogin(
                login->login.loginPage("/login").permitAll()
                .defaultSuccessUrl("/", false) // 성공시 메인 페이지로
                .failureUrl("/login-error")       // 로그인 실패시..
        ).logout(
                logout->logout.logoutSuccessUrl("/") // 로그아웃 시 메인페에지로
        ).exceptionHandling(
                exception->exception.accessDeniedPage("/access-denied")
        );
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .requestMatchers(
                        PathRequest.toStaticResources().atCommonLocations()
                );
    }
}
  • @EnableGlobalMethodSecurity(prePostEnabled = true를 작성해 controller에 정의한 권한 체크 모듈이 동작할 수 있도록한다.
  • roleHierarchy
    • roleHierarchy.setHierarchy(“ROLE_ADMIN > ROLE_USER”) 를 통해 User가 할 수 있는 것은 Admin도 할 수 있도록 한다.
  • formLogin
    • 로그인 성공 시 메인 페이지로 돌아갈 수 있도록 defaultSuccessUrl을 설정
    • 실패할 경우 Url도 설정해준다. → 안해주면 default로 login page로 가짐
  • logout
    • 로그아웃 성공 시 메인 페이지로 가도록 설정
      • 해당 설정을 안하면 logout 성공→ 현재 uri는 /logout →인증 필요→login page 동작으로 login page로 가버린다.
  • exceptionHandling
    • accessDeniedPage를 사용해 권한 없음 등의 처리를

thymeleaf 에서 security를 적용하는 태그

1
2
3
4
5
6
7
8
9
<div sec:authorize="isAuthenticated()">
    Login 해야 보임
</div>
<div sec:authorize="hasRole('ROLE_ADMIN')">
    권한 ADMIN에게만 보임
</div>
<div sec:authorize="hasRole('ROLE_USER')">
    권한 USER에게만 보임
</div>
This post is licensed under CC BY 4.0 by the author.