본문 바로가기

spring boot

JPA / spring boot (로그인 구현, RSA)

일단 로그인을 위한 테이블과 회원가입 테이블을 구분해놨다.

회원가입 테이블엔 유저의 정보만,

로그인 테이블엔 유저네임과 최근 로그인 기록을 남기기 위함이다.

 

먼저 전에 회원가입 부분에서 Bcrypt로 단방향 암호화를 진행했다.

하지만 뷰단에서 비밀번호를 보낼 때에도 암호화가 필요하기에 RSA 를 사용해 양방향 암호화를 진행했다.

@GetMapping(value = "login")
public String login(HttpServletRequest request) {
    Rsa rsa = new Rsa();
    rsa.initRsa(request);

    return "login";
}

 

rsa.init(request); 를 통해 뷰단에 공개키 생성을 위한 RSAModulus, RSAExponent 를 전달했다.

<input type="hidden" name="RSAModulus" id="RSAModulus" th:value="${RSAModulus}">
<input type="hidden" name="RSAExponent" id="RSAExponent" th:value="${RSAExponent}">

 

이를 히든 값으로 받아오고

let rsa = new RSAKey();
rsa.setPublic($('#RSAModulus').val(), $('#RSAExponent').val());
let encryptPassword = rsa.encrypt($('#password').val());
$('#password').val(encryptPassword);

 

받아온 값들로 공개키를 생성, 또한 RSA 형식으로 비밀번호를 암호화 해 요청을 보냈다.

@PostMapping(value = "loginSubmit")
@ResponseBody
public Map<String, Object> loginSubmit(String userName, String password, String userHistory, HttpServletRequest request) throws Exception {
    log.info(userName);
    log.info("============" + password);
    Map<String, Object> result = loginService.loginSubmit(userName, password, userHistory, request);

    return result;
}

 

원래 같았으면 @ModelAttribute 를 사용해서 해당 엔티티 형식으로 받아오면 되지만,

회원가입 테이블과 로그인 테이블 둘다 사용해야 하기에 하나씩 파라미터로 받아왔다.

 

@Service
@Slf4j
@RequiredArgsConstructor
public class LoginService {

    private final JoinRepository joinRepository;
    private final LoginRepository loginRepository;

    public Map<String, Object> loginSubmit(String userName, String password, String userHistory, HttpServletRequest request) throws Exception {
        HttpSession session = request.getSession();
        Optional<JoinEntity> isMember = joinRepository.findByUserName(userName);
        Map<String, Object> map = new HashMap<>();

        if(isMember.isPresent()) {
            Rsa rsa = new Rsa();
            PrivateKey privateKey = (PrivateKey) session.getAttribute(Rsa.RSA_WEB_KEY);
            String userPw = rsa.decryptRsa(privateKey, password);
            String dbPw = isMember.get().getPassword();
            boolean isChecked = BCrypt.checkpw(userPw, dbPw);

            // 현재 날짜, 시간 구해서 userHistory 바인딩
            LocalDateTime now = LocalDateTime.now();
            String formatedNow = now.format(DateTimeFormatter.ofPattern("yyyy/MM/dd/HH/mm/ss"));
            userHistory = formatedNow;

            if(isChecked) {
                Optional<LoginEntity> isDup = loginRepository.findByUserName(userName);

                if(isDup.isPresent()) {
                    // 만약 로그인 기록이 있으면 가져와서 덮어쓴다.
                    LoginEntity loginEntity = isDup.get();
                    loginEntity.setUserName(userName);
                    loginEntity.setUserHistory(userHistory);
                    loginRepository.save(loginEntity);
                }else {
                    // 만약 로그인 기록이 없으면 새로운 생성자를 통해 DB에 추가한다.
                    LoginEntity loginEntity = new LoginEntity();
                    loginEntity.setUserName(userName);
                    loginEntity.setUserHistory(userHistory);
                    loginRepository.save(loginEntity);
                }

                map.put("msg", "Login Success");
                map.put("isOk", 1);
            }else {
                map.put("msg", "Login Failed");
                map.put("isOk", 0);
            }
        }
        return map;
    }
}

 

테이블을 나눠서 회원가입 테이블의 암호화된 비밀번호를 가져와 비교하고

일치한다면 이를 로그인 테이블에서도 로그인 기록이 있다면 덮어쓰고, 없다면 컬럼을 추가하도록 코드를 짰다.

 

 

잘 돌아간다.