본문 바로가기
프로젝트/PongGame

myLogin

by 히포파타마스 2022. 5. 18.

myLogin

Spring security에서는 로그인 처리를 하는 url을 제공해주는데, 그 기본값은 "/login" 이다.

해당 url는 코드상에 노출되어있지 않기 때문에 따로 커스텀하기에 어려움이 있다.

때문에 security의 로그인 처리 과정을 그대로 적용하되, 직접 관련 로직을 커스텀할 수 있도록 개별적인 login API를 만든다.

 

 

 

 

1. API 명세

■ myLogin

username과 password를 받아 인증을 처리하는 API

 

[myLogin]

@PostMapping("/myLogin")
public ResultMessage myLogin(@Valid @RequestBody LoginForm loginForm, HttpServletResponse response) {
}

● 입력

○ loginForm

◎ username

◎ password

 

○ response

인증 성공 시, jwt를 header에 담기 위한 파라미터(클라이언트가 입력할 필요X)

 

 

 

 

2. Form, Validator

■ LoginForm

username과 password를 입력받을 때 사용되는 Form 객체

 

[LoginForm]

@Data
public class LoginForm {

    @Email
    @NotEmpty
    private String username;

    @NotEmpty
    @Length(min = 4, max = 30)
    private String password;
}

 

 

 

■ LoginFormValidator

LoginForm을 검증하는 Validator

 

[LoginFormValidator]

@Component
@RequiredArgsConstructor
public class LoginFormValidator implements Validator {

    private final AccountRepository accountRepository;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz.isAssignableFrom(LoginForm.class);
    }

    @Override
    public void validate(Object target, Errors errors) {

        LoginForm loginForm = (LoginForm) target;

        //[1]
        //** username
        Account account = accountRepository.findByUsername(loginForm.getUsername())
                .orElseThrow(() -> new NonExistResourceException("해당 username을 갖는 Account를 찾을 수 없습니다."));

        //[2]
        //** password
        if (!bCryptPasswordEncoder.matches(loginForm.getPassword(), account.getPassword())) {
            errors.rejectValue("password", "WrongPassword");
        }
    }
}

● [1] : 입력받은 username의 Account가 존재하지 않을 시 error 발생

● [2] : username으로 찾은 Account의 password와 입력된 password가 일치하지 않으면 error 발생

             Account의 password는 암호화돼있기 때문에 bCryptPasswordEncoder.matches()를 사용해서 비교

 

 

 

■ InitBinderControllerAdvice 추가

Controller에 Validator가 적용되도록 기존에 만들어놓은 InitBinderControllerAdvice 클래스에 @InitBinder로 Validator를 적용하는 메서드를 추가한다.

 

[InitBinderControllerAdvice]

@RestControllerAdvice(annotations = RestController.class)
@Component
@RequiredArgsConstructor
public class InitBinderControllerAdvice {

    .
    .
    .
    
    private final LoginFormValidator loginFormValidator;

	.
    .
    .

    //AuthController//
    @InitBinder("loginForm")
    public void loginFormBinder(WebDataBinder webDataBinder) {
        webDataBinder.addValidators(loginFormValidator);
    }
    //AuthController//
}

 

 

 

 

3. service

■ AuthService

인증을 처리하는 Service

 

[AuthService]

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class AuthService {

    private final AuthenticationManager authenticationManager;
    private final JwtProcessor jwtProcessor;

    public String login(String username, String password) {   //[1]
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(username, password);

		//[2]
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        UserAccount userAccount = (UserAccount) authentication.getPrincipal();

        return jwtProcessor.createAuthJwtToken(userAccount);   //[3]
    }
}

● login

○ [1] : usename과 password를 받아서, 실제 Spring Security에서 인증이 이루어지는 것처럼 처리되도록 하였다.

○ [2] : AuthenticationManager.authenticate()로 인증을 진행하며 인증된 객체를 직접 SecurityContextHolder에 넣어줘서

             Spring Security에서 인증 처리된 것과 동일하게 만들어준다.

○ [3] : 인증 처리가 완료되면 JwtToken을 생성해서 반환해준다.

 

 

 

 

4. controller

■ AuthController

[AuthController]

@RestController
@RequiredArgsConstructor
public class AuthController {

    private final AuthService authService;

    @PostMapping("/myLogin")
    public ResultMessage myLogin(@Valid @RequestBody LoginForm loginForm, HttpServletResponse response) {
        String jwtToken = authService.login(loginForm.getUsername(), loginForm.getPassword());
        
        //[1]
        response.addHeader(JwtProperties.HEADER_STRING, JwtProperties.TOKEN_PREFIX + " " + jwtToken);

        return new ResultMessage("success login");
    }
}

● myLogin

○ 로그인을 처리하는 컨트롤러

○ [1] : 인증 성공 시 생성되는 JwtToken을 헤더에 넣는다.

 

 

 

■ sucessfulAuthentication 삭제

sucessfulAuthentication은 Spring Security에서 인증 성공 시 실행되는 메서드로, 인증이 성공하면 JwtToken을 생성하고 응답 헤더에 넣어주도록 Override 하였다.

그러나 현재 인증(Login)에 대한 API에서 별도로 인증과 JwtToken 생성, 헤더 작성을 처리하므로 해당 메서드는 사실상 사용되지 않는다.

때문에 기존에 작성했던 sucessfulAuthentication을 삭제한다.

 

[sucessfulAuthentication]

@RequiredArgsConstructor
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
	
    .
    .
    .

//    @Override
//    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
//                                            Authentication authResult) {
//        UserAccount userAccount = (UserAccount) authResult.getPrincipal();
//
//        String jwtToken = jwtProcessor.createAuthJwtToken(userAccount);
//
//        response.addHeader(JwtProperties.HEADER_STRING, JwtProperties.TOKEN_PREFIX + " " + jwtToken);
//        return authenticationManager.authenticate(authenticationToken);
//    }
}

 

 

 

'프로젝트 > PongGame' 카테고리의 다른 글

RelationRepository  (0) 2022.06.01
Relation API 명세  (0) 2022.05.30
Account Controller  (0) 2022.05.11
Account Service  (0) 2022.05.11
Account_exceptionHandler  (0) 2022.05.11

댓글