@AuthenticationPricipal를 사용해 로그인한 사용자 정보를 받아올 수 있다.
하지만, @AuthenticationPricipal 타입은 UserDetails이기 때문에 커스텀을 해줘야 한다.
1. 기존 Security + JWT 설정 방법
2022.10.25 - [Java] - [Spring Boot] Spring Security JWT를 사용한 토큰 인증 구현
2. UserDetails을 커스텀한 클래스를 작성한다.
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomUserDetails implements UserDetails {
private String username;
private String password;
private String authority;
private boolean enabled;
private Collection<? extends GrantedAuthority> authorities;
private boolean isEnabled;
private boolean isAccountNonExpired;
private boolean isAccountNonLocked;
private boolean isCredentialsNonExpired;
/* 추가 */
private String user_name;
private String nickname;
private int phone;
}
- UserDetails 인터페이스를 상속받고, 추가하고 싶은 변수들을 작성한다.
2. TokenProvider.java (JWT 토큰 발행)
매번 DB접속을 하지 않기 위해 토큰에 기본 정보를 담았다.
@Slf4j
@Service
public class TokenProvider {
// 설정한 토큰 값
@Value("${SECRET_KEY}")
private String SECRET_KEY;
public String create(UserEntity userEntity) {
// 기한은 지금부터 1일로 설정
Date expiryDate = Date.from(
Instant.now()
.plus(1, ChronoUnit.DAYS));
// JWT Token 생성
return Jwts.builder()
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.claim("id", userEntity.getId())
.claim("user_name", userEntity.getUser_name())
.claim("nickname", userEntity.getNickname())
.claim("phone", userEntity.getPhone())
.claim("role", userEntity.getRole())
.setSubject(userEntity.getId())
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.compact();
}
/**
* 토큰의 Claim 디코딩
*/
public Claims getAllClaims(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
}
3. JwtAuthenticationFilter.java (JWT 인증 필터)
토큰이 가지고 있는 정보를 가지고 커스텀했던 UserDetail 객체를 만든다.
@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private TokenProvider tokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
// 요청에서 토큰 가져오기
String token = parseBearerToken(request);
// 토큰 검사하기. JWT이므로 인가 서버에 요청하지 않고도 검증 가능
if(token != null && !token.equalsIgnoreCase(null)) {
// 토큰 정보를 가져온다.
Claims claims = tokenProvider.getAllClaims(token);
// 커스텀한 UserDetail 객체를 생성한다
CustomUserDetails userDetails = CustomUserDetails.builder()
.username((String) claims.get("id"))
.nickname((String) claims.get("nickname"))
.user_name((String) claims.get("user_name"))
.password((String) claims.get("password"))
.phone((int) claims.get("phone"))
.authorities(Arrays.asList(new SimpleGrantedAuthority((String) claims.get("role"))))
.isAccountNonExpired(true)
.isAccountNonLocked(true)
.isCredentialsNonExpired(true)
.build();
//인증 완료. SecurityContextHolder에 등록해야 인증된 사용자라고 생가한다.
AbstractAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails,
null,
AuthorityUtils.NO_AUTHORITIES
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authentication); // 인증 정보 넣기
SecurityContextHolder.setContext(securityContext); // 다시 등록
}
} catch (Exception ex) {
logger.error("시큐리티 필터 오류", ex);
}
filterChain.doFilter(request, response);
}
private String parseBearerToken(HttpServletRequest request) {
// Http 요청의 헤더를 파싱해 Bearer 토큰을 리턴한다.
String bearerToken = request.getHeader("Authorization");
if(StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
4. Controller에 로그인 정보 가져오기
@AuthenticationPricipal을 활용하여 CustomUserDetails를 가져온다.
@Slf4j
@RestController
@RequestMapping("comment")
public class CommentsController {
...
@PostMapping("/new")
public ResponseEntity<?> commentsInsert(@AuthenticationPrincipal CustomUserDetails user, @RequestBody CommentDTO dto){
try {
dto.setUser_id(user.getNickname());
List<Map> comments = service.commentCreate(dto);
return ResponseEntity.ok().body(comments);
} catch (HttpClientErrorException e) {
String error = e.getMessage();
ResponseDTO<CommentDTO> response = ResponseDTO.<CommentDTO>builder().error(error).build();
return ResponseEntity.badRequest().body(response);
}
}
}
반응형
'Java' 카테고리의 다른 글
[Spring Boot] Spring Batch + scheduler 사용해 일정 주기로 실행 방법 (0) | 2023.03.24 |
---|---|
[Spring Boot / Java8] OpenAI API GPT3 사용 방법 (0) | 2023.03.23 |
[Spring Boot] Spring Data JPA에서 Pageable사용한 Pagination 및 정렬 처리 방법 (0) | 2023.03.06 |
[Spring Boot] RestTemplate/WebClient 사용한 REST API 호출 방법 (0) | 2023.02.11 |
[Spring Boot] CORS 설정 방법 (0) | 2022.10.27 |