JSESSIONID값에 매핑되어 사용자에게 배정되는 쓰레드 SecurityContext에 배정되게 됨.@Controller
public class MainController {
@GetMapping("/")
public String mainPage(Model model) {
String id = SecurityContextHolder.getContext().getAuthentication().getName();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iter = authorities.iterator();
GrantedAuthority auth = iter.next();
String role = auth.getAuthority();
model.addAttribute("id", id);
model.addAttribute("role", role);
return "main";
}
}
SecurityContextHolder.getContext().getAuthentication().getName();
SecurityContextHolder.getContext().getAuthentication();
authentication.getAuthorities();
과정.


application.properties
# 단위 : '초'
server.servlet.session.timeout=1800
# 단위 : '분' (끝에 m을 붙이면 됨.)
server.servlet.session.timeout=90m
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.sessionManagement(session -> session
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
);
return http.build();
}

Ex@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.sessionManagement((session) -> session
.sessionFixation((sessionFixation) -> sessionFixation
.newSession()
)
);
return http.build();
}
OST 요청에서 설정 방법.(mustache 기준)
<form action="/login" method="post" name="loginForm">
<input type="text" name="username" placeholder="아이디"/>
<input type="password" name="password" placeholder="비밀번호"/>
<input type="hidden" name="_csrf" value="{{_csrf.token}}"/>
<input type="submit" value="로그인"/>
</form>
<input>태그를 넣음.value="{{_csrf.token}}"ajax와 같이 비동기 상태 요청시
<head> 구획에 아래 요소 추가<meta name="_csrf" content="{{_csrf.token}}"/>
<meta name="_csrf_header" content="{{_csrf.headerName}}"/>
ajax 요청시 위의 content 값을 가져온 후 함께 요청
XMLHttpRequest 요청시 setRequestHeader를 통해 _csrf, _csrf_header Key에 대한 토큰 값 넣어 요청.
.logout() // 로그아웃 설정
.logoutUrl("/logout") // 기본 로그아웃 URL
.logoutSuccessUrl("/") // 로그아웃 성공 후 리다이렉트 URL
.invalidateHttpSession(true) // 세션 무효화
.deleteCookies("JSESSIONID");@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http
.logout((auth) -> auth.logoutUrl("/logout")
.logoutSuccessUrl("/"));
return http.build();
}
@Controller
public class logoutController {
@GetMapping("/logout")
public String logout(HttpServletRequest request, HttpServletResponse response) throws Exception {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null) {
new SecurityContextLogoutHandler().logout(request, response, authentication);
}
return "redirect:/";
}
}
spring.mustache.servlet.expose-request-attributes=true
※ 앱에서 사용하는 API 서버의 경우 보통 세션을 STATELESS로 관리하기 때문에 스프링 시큐리티 csrf enable 설정을 진행하지 않아도 됨.@Configuration
@EnableWebSecurity
public class SecurityConfig {
.....
@Bean
public UserDetailsService userDetailsService() {
UserDetails user1 = User.builder()
.username("userA")
.password(bCryptPasswordEncoder().encode("user1"))
.roles("USER")
.build();
UserDetails user2 = User.builder()
.username("userB")
.password(bCryptPasswordEncoder().encode("user2"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user1, user2);
}
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// throws Exception 적는 이유 : authorizeHttpRequests, build가 예외를 던지고 있음.
httpSecurity
.....
.formLogin((formLogin) -> formLogin
.loginPage("/login").loginProcessingUrl("/loginProc").permitAll()
);
return httpSecurity.build();
}
↓ 수정된 코드
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// throws Exception 적는 이유 : authorizeHttpRequests, build가 예외를 던지고 있음.
httpSecurity
.....
.httpBasic(Customizer.withDefaults());
return httpSecurity.build();
}

/admin에 접근 시.
A < B < C라고 설정하고 싶을 경우 RoleHierarchy를 이용하면 됨.기존 코드.
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// throws Exception 적는 이유 : authorizeHttpRequests, build가 예외를 던지고 있음.
httpSecurity
// CSRF 비활성화.
.csrf((csrf) -> csrf.disable()
)
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/login").permitAll()
.requestMatchers("/").hasAnyRole("A", "B", "C")
.requestMatchers("/manager").hasAnyRole("B", "C")
.requestMatchers("/admin").hasAnyRole("C")
)
// 권한이 필요한 URL 요청 시 /login으로 리다이렉트 시킴.
.httpBasic(Customizer.withDefaults());
return httpSecurity.build();
}
@Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
hierarchy.setHierarchy("ROLE_C > ROLE_B\n" +
"ROLE_B > ROLE_A");
return hierarchy;
}
수정된 코드.
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// throws Exception 적는 이유 : authorizeHttpRequests, build가 예외를 던지고 있음.
httpSecurity
// CSRF 비활성화.
.csrf((csrf) -> csrf.disable()
)
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/login").permitAll()
.requestMatchers("/").hasAnyRole("A")
.requestMatchers("/manager").hasAnyRole("B")
.requestMatchers("/admin").hasAnyRole("C")
.anyRequest().authenticated()
)
// 권한이 필요한 URL 요청 시 /login으로 리다이렉트 시킴.
.httpBasic(Customizer.withDefaults());
return httpSecurity.build();
}
@Bean
public RoleHierarchy roleHierarchy() {
return RoleHierarchyImpl.fromHierarchy(
"""
ROLE_C > ROLE_B
ROLE_B > ROLE_A
"""
);
}
@Bean
public RoleHierarchy roleHierarchy() {
return RoleHierarchyImpl.withRolePrefix("접두사_")
.role("C").implies("B")
.role("B").implies("A")
.build();
}
@Bean
public RoleHierarchy roleHierarchy() {
return RoleHierarchyImpl.withDefaultRolePrefix()
.role("C").implies("B")
.role("B").implies("A")
.build();
}