springBoot
[Spring Boot] Spring Security + JWT token 로그인
뇽꾸리
2022. 9. 6. 17:58
반응형
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
//h2
.headers()
.frameOptions()
.sameOrigin()
// 시큐리티는 기본적으로 세션을 사용
// 여기서는 세션을 사용하지 않기 때문에 세션 설정을 Stateless 로 설정
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
//권한
.authorizeRequests()
.antMatchers("/resources/**","/css/**", "/images/**","/js/**", "/h2-console/**", "/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**")
.permitAll()
.antMatchers("/user/**").hasRole(Role.USER.name()) //user는 유저만
.antMatchers("/admin/**").hasRole(Role.ADMIN.name()) //admin은 관리자만
.anyRequest().authenticated() // 나머지 API 는 전부 인증 필요
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider,userDetailService), UsernamePasswordAuthenticationFilter.class)
;
}
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends GenericFilterBean {
private final JwtTokenProvider jwtTokenProvider;
private final CustomUserDetailService userDetailService;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 1. header 에서 토큰 가져오기
String token = jwtTokenProvider.resolveToken((HttpServletRequest) request);
// 2. 유효한 토큰인지 확인합니다.
if (token != null && jwtTokenProvider.validateToken(token)) {
// 3. 토큰이 유효하면 토큰으로부터 유저 정보를 받아옵니다.
UserDetails userDetails = userDetailService.loadUserByUsername(jwtTokenProvider.getUserPk(token));
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
// 4. Authentication 을 가져와서 SecurityContext 에 저장
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
@Component
@RequiredArgsConstructor
public class JwtTokenProvider {
@Value("${jwt.secret}")
private static final String secretKey; //application.properties 혹은 yml에 설정
private static final long tokenValidTime = 30 * 60 * 1000L;
@PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
// JWT 토큰 생성
public String createToken(String memberId, List<GrantedAuthority> roles) {
Claims claims = Jwts.claims().setSubject(memberId); // JWT payload 에 저장되는 정보단위
claims.put("roles", roles); // 정보는 key / value 쌍으로 저장된다.
Date now = new Date();
return Jwts.builder().setClaims(claims) // 정보 저장
.setIssuedAt(now) // 토큰 발행 시간 정보
.setExpiration(new Date(now.getTime() + tokenValidTime)) // set Expire Time
.signWith(SignatureAlgorithm.HS256, secretKey) // 사용할 암호화 알고리즘과 // signature 에 들어갈 secret값 세팅
.compact();
}
// 토큰에서 회원 정보 추출
public String getUserPk(String token) {
if(Pattern.matches("^Bearer .*",token)){
token = token.replaceAll("^Bearer( )*","");
}
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
// Request의 Header에서 token 값을 가져옵니다. "AUTH-TOKEN" : "TOKEN값'
public String resolveToken(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if(token != null){
if(Pattern.matches("^Bearer .*",request.getHeader("Authorization"))){
token = token.replaceAll("^Bearer( )*","");
}
}
return token;
}
// 토큰의 유효성 + 만료일자 확인
public boolean validateToken(String jwtToken) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken);
return !claims.getBody().getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}
}
@Component
@RequiredArgsConstructor
public class CustomUserDetailService implements UserDetailsService {
private final MemberRepository memberRepository;
@Override
public UserDetails loadUserByUsername(String memberId) throws UsernameNotFoundException {
Member member = memberRepository.findById(Long.valueOf(memberId)).orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));
UserDetails userDetail = User.withUsername(memberId).password(member.getPassword() != null? member.getPassword() : member.getName() ).authorities(member.getRoleKey()).build();
return userDetail;
}
}
반응형