Spring Boot (30) 스프링 시큐리티 실습 회원가입, 로그인 구현하기

넙데데맨·2022년 12월 23일
0

회원가입 및 로그인을 처리할 서비스단과 컨트롤러를 만들어준다.

SignService.java

public interface SignService {
    SignUpResultDto signUp(String id, String password, String name, String role);
    // 회원가입
    SignInResultDto signIn(String id, String password) throws RuntimeException;
    // 로그인
}

SignServiceImpl.java

@Service
public class SignServiceImpl implements SignService {
    private final Logger LOGGER = LoggerFactory.getLogger(SignServiceImpl.class);

    public UserRepository userRepository;
    public JwtTokenProvider jwtTokenProvider;
    public PasswordEncoder passwordEncoder;

    @Autowired
    public SignServiceImpl(UserRepository userRepository, JwtTokenProvider jwtTokenProvider, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.jwtTokenProvider = jwtTokenProvider;
        this.passwordEncoder = passwordEncoder;
    }

회원가입 메소드

    @Override
    public SignUpResultDto signUp(String id, String password, String name, String role) {
        LOGGER.info("[getSignUpResult] 회원 가입 정보 전달");
        User user;
        if(role.equalsIgnoreCase("admin")){ // role이 admin일 경우 ROLE_ADMIN
            user = User.builder()
                    .uid(id)
                    .name(name)
                    .password(passwordEncoder.encode(password))
                    .roles(Collections.singletonList("ROLE_ADMIN"))
                    .build();
        }else{
            user = User.builder()
                    .uid(id)
                    .name(name)
                    .password(passwordEncoder.encode(password))
                    .roles(Collections.singletonList("ROLE_USER"))
                    .build();
        }

        User savedUser = userRepository.save(user);
        SignUpResultDto signUpResultDto = new SignUpResultDto();
        
        LOGGER.info("[getSignUpResult] userEntity 값이 들어왔는지 확인 후 결과값 주입");
        if(!savedUser.getName().isEmpty()){
            LOGGER.info("[getSignUpResult] 정상 처리 완료");
            setSuccessResult(signUpResultDto);
        }else{
            LOGGER.info("[getSignUpResult] 실패 처리 완료");
            setFailResult(signUpResultDto);
        }
        return signUpResultDto;
    }

권한(String role)에 따라 User의 권한을 다르게 저장한다.
password는 암호화를 위해 passwordEncoder를 통해 인코딩 한 후 저장한다.
User 엔티티의 rolesList기 때문에 Collections.singletoneList()를 사용해 권한을 넣어준다.

Collections.singletoneList()

결과값을 List형태로 바꿀 때 사용한다.
Arrays.asList 역시 배열을 리스트로 변환하는 데 원소가 하나일 때는 Collections.singletoneList()를 사용한다.

PasswordEncoder 설정

PasswordEncoder를 사용하기 위한 설정을 해준다.

@Configuration
public class PasswordEncoderConfiguration {
    @Bean
    public PasswordEncoder passwordEncoder(){
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

회원가입 및 인증 성공 및 실패 메소드


    private void setSuccessResult(SignUpResultDto result){
        result.setSuccess(true);
        result.setCode(CommonResponse.SUCESSS.getCode());
        result.setMsg(CommonResponse.SUCESSS.getMsg());

    }
    private void setFailResult(SignUpResultDto result){
        result.setSuccess(false);
        result.setCode(CommonResponse.FAIL.getCode());
        result.setMsg(CommonResponse.FAIL.getMsg());
    }
}

CommonResponse 클래스

public enum CommonResponse {
    SUCESSS(0,"Success"),FAIL(-1,"Fail");

    int code;
    String msg;

    CommonResponse(int code, String msg){
        this.code = code;
        this.msg = msg;
    }

    public int getCode(){
        return code;

    }
    public String getMsg(){
        return msg;
    }
}

로그인 메소드

    @Override
    public SignInResultDto signIn(String id, String password) throws RuntimeException {
        LOGGER.info("[getSignInResult] signDataHandler로 회원 정보 요청");
        User user = userRepository.getByUid(id);
        LOGGER.info("[getSignInResult] Id : {}",id);
        
        LOGGER.info("[getSignInResult] 패스워드 비교 수행");
        if(!passwordEncoder.matches(password,user.getPassword())){
            throw new RuntimeException();
        }
        LOGGER.info("[getSignInResult] 패스워드 일치");

        LOGGER.info("[getSignInResult] SignInResultDto 객체 생성");
        SignInResultDto signInResultDto = SignInResultDto.builder()
                .token(jwtTokenProvider.createToken(String.valueOf(user.getUid()),user.getRoles()))
                .build();
        
        LOGGER.info("[getSignInResult] SignInResultDto 객체에 값 주입");
        setSuccessResult(signInResultDto);
        return signInResultDto;
    }

User의 id로 user 정보를 불러온 후 전달된 패스워드와 비교를 수행한다.
불일치할 시 RuntimeException을 발생 시키며 signInResultDto에 토큰을 생성한다.

SignUpResultDto, SignInResultDto

SignUpResultDto

성공 여부를 알려주는 success와 http 상태코드 메시지를 저장하는 회원가입 dto

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class SignUpResultDto {
    private boolean success;
    private int code;
    private String msg;
}

SignInResultDto

로그인 성공 후 토큰값을 담을 dto

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class SignInResultDto extends SignUpResultDto{
    private String token;

    @Builder
    public SignInResultDto(boolean success, int code, String msg, String token){
        super(success, code, msg);
        this.token = token;
    }
}

컨트롤러

SignController

@RestController
@RequestMapping("/sign-api")
public class SignController {
    private final Logger LOGGER = LoggerFactory.getLogger(SignController.class);
    private final SignService signService;

    @Autowired
    public SignController(SignService signService){
        this.signService = signService;
    }

    @PostMapping(value="/sign-in")
    public SignInResultDto signIn(
            @ApiParam(value = "ID", required = true) @RequestParam String id,
            @ApiParam(value = "Password", required = true) @RequestParam String password
    ) throws RuntimeException    {
        LOGGER.info("[signIn] 로그인을 시도하고 있습니다. id : {}, pw : ****",id);
        SignInResultDto signInResultDto = signService.signIn(id,password);

        if(signInResultDto.getCode()==0){
            LOGGER.info("[signIn] 정상적으로 로그인되었습니다. id:{},token:{}",id,signInResultDto.getToken());
        }
        return signInResultDto;
    }

    @PostMapping(value="/sign-up")
    public SignUpResultDto signUp(
            @ApiParam(value = "ID", required = true) @RequestParam String id,
            @ApiParam(value = "비밀번호", required = true) @RequestParam String password,
            @ApiParam(value = "이름", required = true) @RequestParam String name,
            @ApiParam(value = "권한", required = true) @RequestParam String role
    ){
        LOGGER.info("[signUp] 회원가입을 수행합니다. id : {}, password:****,name:{},role:{}",id,name,role);
        SignUpResultDto signUpResultDto = signService.signUp(id,password,name,role);
        return signUpResultDto;
    }

    @GetMapping(value = "/exception")
    public void exceptionTest() throws RuntimeException{
        throw new RuntimeException("접근이 금지되었습니다.");
    }

    @ExceptionHandler(value = RuntimeException.class)
    public ResponseEntity<Map<String,String>> ExceptionHandler(RuntimeException e){
        HttpHeaders responseHeader = new HttpHeaders();
        // responseHeaders.add(HttpHeaders.CONTENT_TYPE,"application/json");
        HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
        LOGGER.error("ExceptionHandler 호출, {}, {}", e.getCause(),e.getMessage());

        Map<String,String> map = new HashMap<>();
        map.put("error type", httpStatus.getReasonPhrase());
        map.put("code", "400");
        map.put("message", "에러발생");

        return new ResponseEntity<>(map,responseHeader,httpStatus);
    }
}

각각 로그인, 회원가입, 오류가 발생해 리디렉션될 exception, RuntimeException 발생 시 이를 처리해주는 핸들러로 구성되어있다.

profile
차근차근

0개의 댓글