BE전문가 프로젝트

4-1 Authentication(인증) - UserDetails, UserDetailsService 및 JwtService 생성 본문

SpringBoot Security

4-1 Authentication(인증) - UserDetails, UserDetailsService 및 JwtService 생성

원호보고서 2023. 7. 9. 20:21

LoginDto 생성

public class LoginDto {

    @Getter
    public static class LoginRequestDto {

        private String email;
        private String password;
    }

    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class LoginResponseDto {

        private long userId;
        private String email;
        private String password;
        private String nickName;
        private String profileUrl;
        private String refreshToken;
        private String userRole;
        private SocialType socialType;

        public List<String> getRoleList(){
            if(this.userRole.length() > 0){
                return Arrays.asList(this.userRole.split(","));
            }
            return new ArrayList<>();
        }

    }

login에서 사용할 Dto를 만들어 준다.

 

public List<String> getRoleList(){
    if(this.userRole.length() > 0){
        return Arrays.asList(this.userRole.split(","));
    }
    return new ArrayList<>();
}
  • UserDetail에서 권한을 가져올 때 Return 타입이 Collection이기 때문에 만들어준 함수

 

PrincipalDetails생성

/**
 * @package com.example.spring_security.config.security.auth
 * @class   PrincipalDetails
 * @brief   권합 삽입
 * @details 사용자에 해당하는 권한을 넣어준다
 *          시큐리티가 가지고 있는 시큐리티_session에 들어갈 수 있는 Object는 정해져 있음(Object == Authentication객체)
 * @author  최원호
 * @date    2023.05.02
 * version  1.0
 */
public class PrincipalDetails implements UserDetails {

    private LoginResponseDto loginResponseDto;

    public PrincipalDetails(LoginResponseDto loginResponseDto) {

        this.loginResponseDto = loginResponseDto;
    }

    /**
     * @details 사용자 정보에 권한을 삽입한다.
     * @return  권한
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        loginResponseDto.getRoleList().forEach(r -> {
            authorities.add(()->{ return r;});
        });

        return authorities;
    }

    /**
     * @brief 패스워드 호출.
     * @return  패스워드
     */
    @Override
    public String getPassword() {
        return loginResponseDto.getPassword();
    }

    /**
     * @brief 유저네임 호출.
     * @return  이메일
     */
    @Override
    public String getUsername() {
        return loginResponseDto.getEmail();
    }

    /**
     * @brief 계정의 만료 여부 리턴
     * @return  true
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * @brief 계정의 잠금 여부 리턴
     * @return  true
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * @brief 비밀번호 만료 여부 리턴
     * @return  true
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * @brief 계정의 활성화 여부 리턴
     * @return  true
     */
    @Override
    public boolean isEnabled() {
        return true;
    }
}

 

UserDetails

  • Spring Security에서 사용자의 정보를 담는 인터페이스

 

PrincipalDetailsService생성

/**
 * @package com.example.spring_security.config.security.auth
 * @class   PrincipalDetailsService
 * @brief   사용자의 정보를 가져오기 위한 비즈니스 로직
 * @author  최원호
 * @date    2023.06.22
 * version  1.0
 */

@Slf4j
@Service
public class PrincipalDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

        User user = userRepository.findByEmail(email).orElseThrow();

        LoginResponseDto loginResponseDto = LoginResponseDto.builder()
                .userId(user.getUserId())
                .email(user.getEmail())
                .password(user.getPassword())
                .nickName(user.getNickname())
                .profileUrl(user.getProfileUrl())
                .refreshToken(user.getRefreshToken())
                .userRole(user.getUserRole())
                .build();

        return new PrincipalDetails(loginResponseDto);
    }
}

UserDetailsService

  • Spring Security에서 유저의 정보를 가져오는 인터페이스

 

JwtService생성

package com.example.spring_security.config.security.jwt.Service;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.spring_security.config.security.jwt.JwtProperties;
import com.example.spring_security.config.security.jwt.dto.LoginDto.LoginResponseDto;
import com.example.spring_security.user.domain.User;
import com.example.spring_security.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

/**
 * @package     com.example.spring_security.config.security.jwt.Service
 * @class   JwtServiceImpl
 * @brief       jwt 비즈니스로직 구현 클래스
 * @author      최원호
 * @date        2023.06.22
 * version      1.0
 */

@Slf4j
@Service
@RequiredArgsConstructor
public class JwtServiceImpl implements JwtService {

    @Value("${jwt.secret}")
    private String SECRET_KEY;

    private final UserRepository userRepository;

    /**
     * @brief   토큰 생성
     * @param   email
     * @return  accessToken
     */
    @Override
    public String createAccessToken(String email) {

        return JWT.create()
                .withSubject(JwtProperties.ACCESS_TOKEN)
                .withClaim(JwtProperties.EMAIL, email)
                .withExpiresAt(new Date(System.currentTimeMillis()+JwtProperties.EXPIRATION_TIME))
                .sign(Algorithm.HMAC512(SECRET_KEY));
    }

    /**
     * @brief   refresh 토큰 생성
     * @return  refreshToken
     */
    @Override
    public String createRefreshToken() {
        return JWT.create()
                .withSubject(JwtProperties.REFRESH_TOKEN)
                .withExpiresAt(new Date(System.currentTimeMillis()+JwtProperties.EXPIRATION_TIME))
                .sign(Algorithm.HMAC512(SECRET_KEY));
    }

    /**
     * @brief   email을 통한 유저 entity 조회
     * @param   email
     * @return  LoginResponseDto
     */
    @Override
    public LoginResponseDto retrieveByEmail(String email) {

        User user = userRepository.findByEmail(email).orElseThrow();

        return LoginResponseDto.builder().userId(user.getUserId())
                .email(user.getEmail())
                .password(user.getPassword())
                .nickName(user.getNickname())
                .profileUrl(user.getProfileUrl())
                .refreshToken(user.getRefreshToken())
                .userRole(user.getUserRole())
                .socialType(user.getSocialType())
                .build();
    }

    /**
     * @brief   refreshToken 수정
     * @param   userId
     * @param   refreshToken
     */
    @Override
    @Transactional
    public void setRefreshToken(long userId, String refreshToken) {

        userRepository.findById(userId).orElseThrow().updateRefreshToken(refreshToken);
    }

    /**
     * @brief   accessToken Header에 설정
     * @param   response
     * @param   accessToken
     */
    @Override
    public void setAccessTokenToHeader(HttpServletResponse response, String accessToken) {
        response.setHeader(JwtProperties.ACCESS_TOKEN_HEADER,JwtProperties.TOKEN_PREFIX + accessToken);
    }

    /**
     * @brief   refreshToken Header에 설정
     * @param   response
     * @param   refreshToken
     */
    @Override
    public void setRefreshTokenToHeader(HttpServletResponse response, String refreshToken) {
        response.setHeader(JwtProperties.REFRESH_TOKEN_HEADER, JwtProperties.TOKEN_PREFIX + refreshToken);
    }

    /**
     * @brief   결과 메시지 구현
     * @param   result
     * @param   response
     * @param   message
     */
    @Override
    public void setResponseMessage(boolean result, HttpServletResponse response, String message) throws IOException {

        JSONObject jObject = new JSONObject();
        response.setContentType("application/json;charset=UTF-8");

        if(result){
            response.setStatus(HttpServletResponse.SC_OK);
            jObject.put("success", true);
            jObject.put("code", 200);
            jObject.put("message", message);
        } else {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            jObject.put("success", false);
            jObject.put("code", 999);
            jObject.put("message", message);
        }
        response.getWriter().print(jObject);
    }
}

 

 

@Override
public String createAccessToken(String email) {

    return JWT.create()
            .withSubject(JwtProperties.ACCESS_TOKEN)
            .withClaim(JwtProperties.EMAIL, email)
            .withExpiresAt(new Date(System.currentTimeMillis()+JwtProperties.EXPIRATION_TIME))
            .sign(Algorithm.HMAC512(SECRET_KEY));
}
  • withSubject: 토큰의 이름
  • withClaim: 토큰에 담고싶은 정보
  • withExpiresAt: 토큰의 유효기간
  • sign: 토큰의 암호화 알고리즘

 

Comments