JWT 인증 방식의 실현

5573 단어 Java 학습
JWT는 Json Web Token이라고 불리며 현재 비교적 유행하는 인증 방식이다.전통적인session인증방식에 비해 jwt는 디자인 초기에'무상태'를 강조했다. 즉, 서버는 어떠한 인증정보도 저장하지 않고 영패를 발급하는 역할만 한다.본고는 구체적인 실현 방법을 중점적으로 논술하고자 한다.

기본 프로세스


1, 우선 서버에서 로그인 요청을 받았을 때 요청 헤더에 영패가 있는지 확인합니다(첫 로그인은 반드시 없습니다).서버는 계정 비밀번호가 모두 통과된 것을 검증하는 상황에서 token(즉 영패)을 생성하여 응답 헤더에 추가하여 클라이언트에게 되돌려줍니다.2, 클라이언트가 token을 받은 후에 모든 요청은 요청 헤더에 이 token을 가지고 와서 신분을 검증해야 합니다.3, token이 효력을 잃었을 때 서버는 클라이언트에게 영패가 만료되었다고 통지하고 로그인 요청을 다시 보내서 새로운 token을 가져옵니다.

필요한 의존


	com.auth0
	java-jwt
	3.1.0


생성token

public static String getToken(final String userId,final String role) {
        String token = null;
        try {
            Long currentTime = System.currentTimeMillis();
            String tokenId = new StringBuilder(String.valueOf(currentTime)).append(userId).toString();
            Date expiresAt = new Date(currentTime + 10L * 60L * 1000L);
            token = JWT.create()
            		//  
                    .withIssuer("")
                    //  
                    .withClaim("userId", userId)
                    .withClaim("role",role)
                    //  
                    .withIssuedAt(new Date(currentTime))
                    //  id
                    .withJWTId(tokenId)
                    // token 
                    .withExpiresAt(expiresAt)
                    //  HMAC256 。
                    .sign(Algorithm.HMAC256("SECRET_KEY"));
        } catch (JWTCreationException exception){
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return token;
    }

그중.sign(Algorithm.HMAC256("SECRET_KEY")에서 HMAC256 방법으로 서버에 설정된 키를 암호화하여 token이 위조되는 것을 방지합니다.따라서 이 키는 반드시 잘 저장해야 한다. 유출되면 클라이언트가 임의로 토큰을 서명할 수 있다는 것을 의미한다.

token 검사

public static Boolean decryptToken(final String token) {
        if (token == null){
            return false;
        }
        DecodedJWT jwt = null;
        try {
            //  HMAC256 。
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(GlobalConstant.SECRET_KEY))
                    .withIssuer("")
                    .build(); //Reusable verifier instance
            //  , JWTVerificationException InvalidClaimException
            jwt = verifier.verify(token);
        } catch (JWTVerificationException exception){
            //Invalid signature/claims
            return false;
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return true;
    }

verifier.verify(token)에서 서명의 시간 초과 여부를 검사했습니다. 시간 초과는 InvalidClaimException을 던져 원본 코드를 첨부합니다.
private void assertValidDateClaim(Date date, long leeway, boolean shouldBeFuture) {
        Date today = this.clock.getToday();
        today.setTime((long)Math.floor((double)(today.getTime() / 1000L * 1000L)));
        boolean isValid;
        String errMessage;
        if (shouldBeFuture) {
            today.setTime(today.getTime() - leeway * 1000L);
            isValid = date == null || !today.after(date);
            errMessage = String.format("The Token has expired on %s.", date);
        } else {
            today.setTime(today.getTime() + leeway * 1000L);
            isValid = date == null || !today.before(date);
            errMessage = String.format("The Token can't be used before %s.", date);
        }

        if (!isValid) {
            throw new InvalidClaimException(errMessage);
        }
    }

전송 및 수신 token

	@ApiOperation(" ")
    @PostMapping("login")
    public ResponseData login(@RequestBody Account account, HttpServletRequest request, HttpServletResponse response){
    	//  token
        String token = request.getHeader("Authorization");
        //token , token
        if (token == null){
        
        	//  
        	// ...
        	// 
        	
        	// token
			response.addHeader("Authorization",getToken(user.getUserId(), user.getRole()));
		}else {
            // token
            if(decryptToken(token)){
                //  
            }else {
                // token , 
            }
        }
    }

상기 코드는 기본적으로 로그인 인증을 실현할 수 있습니다. 이외에도 주의해야 할 점이 있습니다. 전방에서 되돌아오는 token을 받을 때 Refused to get unsafe header'Authorization'을 제시합니다. 이것은 Authorization의 header가 우리가 정의한 것이기 때문입니다. 해결 방법은 크로스 처리 부분에 Access-Control-Expose-Headers를 추가하는 것입니다.
        response.setHeader("Access-Control-Expose-Headers","Authorization");

Token 만료에 대한 생각


jwt를 사용하는 인증 방식은 확장하기 쉽지만, 사용자가 로그아웃 작업을 실행한 후, 또는 사용자가 비밀번호를 수정한 후, 현재의 token은 효력을 상실해야 하지만, jwt는 현재의 token을 즉시 효력을 상실할 수 없을 것 같습니다.만약에 서버가 모든 token을 기록하는 방식을 사용한다면 jwt의 디자인 취지와 어긋난다.국내외의 일부 방법을 훑어보았는데 비교적 흔히 볼 수 있는 것은 블랙리스트 목록을 설정하여 버려진 토큰을 블랙리스트에 넣는 것이다.블랙리스트를 실현하는 방법은 매우 간단하다. redis의 특성은 이 수요에 완벽하게 적응할 수 있다. 사용자가 로그인한 후에 우리는 token의 tokenId를 redis 키 값에 맞는 키로 설정하고 token은value로 설정하며 token의 시간 초과 시간을 기한이 지난 시간으로 설정하여 사용 시간이 만료되지 않았지만 버려진 token에 접근하는 문제를 피할 수 있다.

좋은 웹페이지 즐겨찾기