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>