사용자가 로그인하면 JWT가 생성된다. 그걸 쿠키에 넣으면, 브라우저가 이후 요청마다 자동으로 쿠키를 전송한다. 즉, 로그인 이후 다른 페이지를 요청할 때도 서버가 사용자를 식별할 수 있게 되는 것이다.
HttpOnly 속성을 사용하면 자바스크립트로 쿠키에 접근할 수 없다. 로컬스토리지나 세션스토리지에 JWT를 저장하는 방식보다 보안적으로 더 안전한 방식이다.
프론트 코드 수정 없이도 자동으로 서버에 보내져서 인증 절차가 간편해진다.
유저에게 email과 Password를 입력받는다.
반환받은 JWT가 null이 아니라면 쿠키를 생성한다.
DB에 해당 유저가 존재하는지 확인한다.
반환받은 닉네임이 null이 아니라면 JWT를 생성한다.
DB에 해당 유저가 존재하는지 확인한다.
유저가 존재하면 해당 유저의 닉네임을 반환하고 존재하지 않으면 null을 반환한다.
@WebServlet("/user/login")
public class LoginController extends HttpServlet {
private final UserService userService = new UserService();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
UserDto.Login dto = UserDto.Login.from(req);
String jwt = userService.login(dto);
if(jwt != null) {
Cookie cookie = new Cookie("SJB_AT", jwt);
cookie.setHttpOnly(true);
resp.addCookie(cookie);
// 응답에 jwt를 바로 줌 (보안 취약)
//resp.getWriter().write(JsonParser.from(BaseResponse.success(jwt)));
// 쿠키에 담아서 줌
resp.getWriter().write(JsonParser.from(BaseResponse.success(cookie)));
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
public class UserDto {
public static class Login {
private String email;
private String password;
public static UserDto.Login from(HttpServletRequest req) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
UserDto.Login dto = objectMapper.readValue(req.getReader(), UserDto.Login.class);
return dto;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
}
public class UserService {
private final UserRepository userRepository = new UserRepository();
public String login(UserDto.Login dto) throws SQLException {
String nickname = userRepository.findByEmailAndPassword(dto);
if (nickname != null) {
String jwt = JwtUtil.generateToken(dto.getEmail(), nickname);
return jwt;
}
return null;
}
}
public class UserRepository {
public String findByEmailAndPassword(UserDto.Login dto) throws SQLException {
Statement stmt = DBUtil.getStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM user WHERE email='"+dto.getEmail()+"' AND password='"+dto.getPassword()+"'");
if(rs.next()) {
return rs.getString("nickname");
} else {
return null;
}
}
}
package com.example.utils;
public class JwtUtil {
private static final String SECRET = "abcdeffghijklmnopqrstuvwxyz0123456";
private static final Key KEY = Keys.hmacShaKeyFor(SECRET.getBytes());
private static final Long EXP = 1000 * 60 * 1L;
public static String generateToken(String email, String nickname) {
Map<String, String> claims = new HashMap<>();
claims.put("nickname", nickname);
claims.put("role", "USER");
return Jwts.builder()
.setSubject(email)
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + EXP))
.signWith(KEY, SignatureAlgorithm.HS256)
.compact();
}
public static String getRole(Claims claims) {
String role = (String) claims.get("role");
return role;
}
public static Claims getClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(KEY)
.build()
.parseClaimsJws(token)
.getBody();
}
}