어차피 따라한거지만 복잡해서 까먹을까봐 적어놓는 구현흐름
장고했을때 회원가입 + 로그인 구현하는것은 그렇게 복잡하지 않았던 것으로 기억한다. 하지만 스프링 하면서 로그인을 해보려니 너무 복잡하고 어려웠다..
버전 신경써야하고 deprecated된 것들이 꽤 많아서 애를 먹었다.
구글링해도 괜찮은 참고자료를 찾기 어려웠는데 우연히
https://velog.io/@tank3a/Security-%EC%84%A4%EC%A0%951
를 알게되었고 이 분을 통해 아래링크의 블로그를 알게되었다
https://bcp0109.tistory.com/301
이 블로그가 최고인것같다
우선 현재 폴더구조는 아래와 같다
원래 하던 프로젝트에 로그인을 구현하기위해 추가한것 들이 많아 무엇을 추가했었는지 작성하도록 하겠다
처음에 프로젝트를 만들 때 추가하지 않았어서 수동으로 추가해줬다
dependencies{
//스프링 시큐리티
implementation 'org.springframework.boot:spring-boot-starter-security'
//JWT
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2'
jwt:
secret: ~~~~
원래 나는 회원을 User로 했었다.
하지만 스프링 시큐리티 자체적으로 UserDetails 의 구현체인 User 를 사용하기 때문에 헷갈리지 않도록 Account 또는 Member 로 이름 짓는게 좋다고 한다.
(처음 알게되었는데 import할 때 진짜 있긴했었다)
결론 : User라는 이름은 웬만해서는 피하는게 좋다. 실제로 import할 때 무지성 option+enter로 추가하다가 내가 domain에 만들어놓은 User 엔티티를 추가한적이 있다
리프레시 토큰 저장하는 엔티티
Member
Member Reposity
Member Service 를 각각 구현해준다
TokenProvider
JwtFilter
JwtSecurityConfig
JwtAuthenticationEntryPoint
JwtAccessDeniedHandler
구현해준다
// Access Token 생성
Date accessTokenExpiresIn = new Date(now + ACCESS_TOKEN_EXPIRE_TIME);
String accessToken = Jwts.builder()
.setSubject(authentication.getName()) // payload "sub": "name"
.claim(AUTHORITIES_KEY, authorities) // payload "auth": "ROLE_USER"
.setExpiration(accessTokenExpiresIn) // payload "exp": 1516239022 (예시)
.signWith(SignatureAlgorithm.HS512, key) // header "alg": "HS512"
.compact();
원래는 위에 .signWith() 안에 key랑 SignatureAlgorithm.HS512가 반대로 되어있었는데 위에처럼 해주니까 에러가 사라짐
https://leeeehhjj.tistory.com/m/61
이분거 보고 참고한건데 이분은 또 나랑 반대로 하니까 됐다고 하신다..
그리고 .signWith 자체가 deprecated 됐다고 하는것 같다
public Authentication getAuthentication(String accessToken) {
// 토큰 복호화
Claims claims = parseClaims(accessToken);
if (claims.get(AUTHORITIES_KEY) == null) {
throw new RuntimeException("권한 정보가 없는 토큰입니다.");
}
// 클레임에서 권한 정보 가져오기
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
// UserDetails 객체를 만들어서 Authentication 리턴
UserDetails principal = new User(claims.getSubject(), "", authorities);
return new UsernamePasswordAuthenticationToken(principal, "", authorities);
}
그리고 또 위에보면 중간에 SimpleGrantedAuthority가 있는데
저거는 추가하라는 메시지가 안떠가지고 아래 것 직접 import해줌
import org.springframework.security.core.authority.SimpleGrantedAuthority;
JwtSecurityConfig
SecurityConfig
SecurityUtil
MemberReposity
RefreshTokenRepository (리프레시 토큰 저장소)
MemberService
AuthService
CustomUserDetailsService
AuthController
MemberController
MemberRequestDto
MemberResponseDto
TokenDto
TokenRequestDto