JSON Web Tokens 의 실현 원리

8011 단어 jsonwebtokens
머리말
최근 에 Python 프로젝트 의 개 조 를 하고 python 프로젝트 를 자바 프로젝트 로 재 구성 하 는 과정 에서 이 지식 을 만 났 습 니 다.이것 은 매우 실 용적 이 라 고 생각 하기 때문에 퇴근 후에 돌아 와 서 쇠뿔 도 단 김 에 이 정 리 를 쓰 고 뒤에 있 는 사람들 이 참고 하여 시행 착 오 를 적 게 걷 기 를 바 랍 니 다.
1.우위 소개
JSON Web Tokens 는 jwt 라 고 부 르 며 rest 인터페이스의 안전 전략 입 니 다.그 자체 에 많은 장점 이 있다.
크로스 도 메 인 문제 해결:이러한 Token 기반 방문 전략 은 cookies 의 크로스 도 메 인 문 제 를 극복 할 수 있 습 니 다.
서버 가 상태 가 없 으 면 가로로 확장 할 수 있 고 Token 은 인증 을 완료 할 수 있 으 며 Session 을 저장 할 필요 가 없습니다.
시스템 디 버 깅,Token 은 모든 사용자 정 보 를 가지 고 있 습 니 다.특정한 인증 방안 을 연결 하지 않 고 암호 화 방법 과 키 만 알 면 암호 화 디 버 깅 을 할 수 있어 디 버 깅 에 유리 합 니 다.
크로스 사이트 스 크 립 트 공격 을 방지 하고 쿠키 기술 이 없 으 며 크로스 사이트 요청 의 안전 문 제 를 고려 할 필요 가 없습니다.
원리 소개
JSON Web Tokens 의 형식 으로 구성 되 어 있 습 니 다.jwt 는 base 64 에 인 코딩 된 문자 시퀀스 로 점 번호 로 구분 되 어 있 으 며 모두 세 부분 으로 구성 되 어 있 습 니 다.머리 header,메시지 체 play load 와 서명 sign 입 니 다.
1.jwt 의 머리 Header 는 json 형식 입 니 다.

{
  "typ":"JWT",
  "alg":"HS256",
  "exp":1491066992916
}
그 중에서 typ 는 type 의 약자 로 이 유형 은 JWT 형식 이 고 암호 화 방식 은 HS 256 이 며 exp 는 현재 시간 을 대표 합 니 다.
2.jwt 메시지 체 Playload

{
  "userid":"123456",
  "iss":"companyName"
}
메시지 체 의 구체 적 인 필드 는 업무 수요 에 따라 자체 적 으로 정의 하고 추가 할 수 있 으 며,복호화 할 때 필드 의 key 값 으로 value 를 가 져 오 는 것 에 주의 하 십시오.
3.서명 sign 생 성
마지막 으로 서명 입 니 다.서명 의 생 성 은 header 와 playload 를 각각 base64url 인 코딩 을 사용 한 다음'.'두 인 코딩 된 문자열 을 연결 한 다음 에 이 연 결 된 문자열 을 키 에 맞 춰 HMAC SHA-256 알고리즘 을 암호 화하 고 마지막 으로 base 64 인 코딩 을 다시 합 니 다.이것 은 서명 sign 을 받 았 습 니 다.마지막 으로 header 와 playload 와 sign 을'연결 하면 전체 JWT 가 생 성 됩 니 다.
3.검사 안내
전체 jwt 의 구 조 는 header.playload.sign 연결 로 구성 되 어 있 으 며,sign 만 키 로 암호 화 되 어 있 으 며,모든 정 보 는 header 와 playload 에서 직접 얻 을 수 있 습 니 다.sign 의 역할 은 header 와 playload 의 정보 가 변경 되 었 는 지 검증 하 는 것 이기 때문에 jwt 는 수 거 를 보호 할 수 없 지만,이상 의 특성 은 권한 인증 에 잘 적 용 될 수 있 습 니 다.
1.암호 화
예 를 들 어 암호 화 검증 할 것 은 userid 필드 입 니 다.먼저 json 메시지 헤더 헤더 와 메시지 체 playload 를 앞의 형식 으로 조립 하고 header.playload 에 따라 문자열 을 구성 한 다음 키 와 HS 256 암호 헤더.playload 에 따라 sign 서명 을 받 고 마지막 으로 jwtToken 을 header.playload.sign 으로 받 습 니 다.http 요청 중의 url 에 인 자 를 가지 고 백 엔 드 서비스 에 인증 을 요청 합 니 다.
2.복호화
백 엔 드 서 비 스 는 jwtToken 이 인터페이스 서 비 스 를 방문 할 권리 가 있 는 지 확인 하고 복호화 인증 을 할 수 있 습 니 다.예 를 들 어 방문 자의 userid 를 검사 할 수 있 습 니 다.먼저
문자열 을.번호 에 따라 세 개의 문자열 로 나 누 어 각각 header 와 playload,sign 을 얻 습 니 다.그 다음 에 header.playload 조합 을 키 와 HAMC SHA-256 알고리즘 으로 암호 화한 다음 에 새로운 문자열 과 sign 을 비교 합 니 다.똑 같 으 면 데이터 가 변경 되 지 않 았 다 는 뜻 입 니 다.그리고 머리 에서 exp 를 꺼 내 생존 기간 을 판단 하고 생존 기간 을 초과 하면 빈 문자열 로 돌아 갑 니 다.생존 기간 내 에 userid 의 값 을 되 돌려 줍 니 다.
코드 예제
1.python 코드 의 암호 화 복호화

#!/usr/bin/env python
# coding: utf-8

from itsdangerous import BadTimeSignature, SignatureExpired
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

APP_SECRET_KEY="secret"
MAX_TOKEN_AGE=1800
token_generator = Serializer(APP_SECRET_KEY, expires_in=MAX_TOKEN_AGE)

def generate_auth_token(userid):
  access_token = token_generator.dumps({"userid":userid})
  return access_token
def verify_token(token):
  try:
    user_auth = token_generator.loads(token)
    print type(token_generator)
  except SignatureExpired as e:
    raise e
  except BadTimeSignature as e:
    raise e
  return user_auth
2.자바 코드 의 암호 화 복호화

package api.test.util;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;

/**
 * jwt     
 * 
 * @author zhengsc
 */
@Slf4j
public class TokenUtil {

  private String ISSUER = "companyName"; //   

  private String APP_SECRET_KEY = "secret"; //   

  private long MAX_TOKEN_AGE = 1800; //    

  /**
   *   userId accessToken
   * 
   * @param userid
   * @return
   */
  public String generateAccessToken(String userid) {
    JSONObject claims = new JSONObject();
    claims.put("iss", ISSUER);
    claims.put("userid", userid);
    String accessToken = sign(claims, APP_SECRET_KEY);
    return accessToken;
  }

  /**
   *       userid
   * 
   * @param token
   * @return
   */
  public String verifyToken(String token) {
    String userid = "";
    try {
      String[] splitStr = token.split("\\.");
      String headerAndClaimsStr = splitStr[0] + "." +splitStr[1];
      String veryStr = signHmac256(headerAndClaimsStr, APP_SECRET_KEY);
      //          
      if (veryStr.equals(splitStr[2])) {
        String header = new String(Base64.decodeBase64(splitStr[0]),"UTF-8");
        JSONObject head = JSONObject.fromObject(header);
        long expire = head.getLong("exp") * 1000L;
        long currentTime = System.currentTimeMillis();
        if (currentTime <= expire){ //   accessToken    
          String claims = new String(Base64.decodeBase64(splitStr[1]),"UTF-8");
          JSONObject claim = JSONObject.fromObject(claims);
          userid = (String) claim.get("userid");
        }
      }
    } catch (UnsupportedEncodingException e) {
      log.error(e.getMessage(), e);
    }

    return userid;
  }

  /**
   *       jwt  
   * 
   * @param claims
   * @param appSecretKey
   * @return
   */
  private String sign(JSONObject claims, String appSecretKey) {
    String headerAndClaimsStr = getHeaderAndClaimsStr(claims);
    String signed256 = signHmac256(headerAndClaimsStr, appSecretKey);
    return headerAndClaimsStr + "." + signed256;
  }

  /**
   *         
   * 
   * @param claims
   * @return
   */
  private String getHeaderAndClaimsStr(JSONObject claims) {
    JSONObject header = new JSONObject();
    header.put("alg", "HS256");
    header.put("typ", "JWT");
    header.put("exp", System.currentTimeMillis() + MAX_TOKEN_AGE * 1000L);
    String headerStr = header.toString();
    String claimsStr = claims.toString();
    String headerAndClaimsStr = Base64.encodeBase64URLSafeString(headerStr.getBytes()) + "."
        + Base64.encodeBase64URLSafeString(claimsStr.getBytes());
    return headerAndClaimsStr;
  }

  /**
   *  headerAndClaimsStr SHA1    sign
   * 
   * @param headerAndClaimsStr
   * @param appSecretKey
   * @return
   */
  private String signHmac256(String headerAndClaimsStr, String appSecretKey) {
    SecretKey key = new SecretKeySpec(appSecretKey.getBytes(), "HmacSHA256");
    String result = null;
    try {
      Mac mac;
      mac = Mac.getInstance(key.getAlgorithm());
      mac.init(key);
      result = Base64.encodeBase64URLSafeString(mac.doFinal(headerAndClaimsStr.getBytes()));
    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
      log.error(e.getMessage(), e);
    }
    return result;
  }

}
이상 은 본 고의 모든 내용 입 니 다.본 고의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.또한 저 희 를 많이 지지 해 주시 기 바 랍 니 다!

좋은 웹페이지 즐겨찾기