JWT (正式名: Json Web Token) は、当事者間で通信するためのコンパクトで自己完結型の方法を JSON オブジェクトとして定義するオープン スタンダード (RFC 7519) です。
各ユーザーのログイン情報はサーバーのセッションに保存されます。ユーザー数が増えると、サーバーのオーバーヘッドが大幅に増加します。
セッション情報はサーバーのメモリに保存されるため、分散アプリケーションでは障害が発生します。セッション情報は Redis キャッシュに均一に保存できますが、複雑さが増す可能性があります。
セッション認証はCookieによるため、非ブラウザ端末やモバイル端末には適用できません。
フロントエンドとバックエンドの分離システム。フロントエンドとバックエンドはクロスドメインであり、Cookie 情報を横断できないため、セッション認証をクロスドメインで続行できません。 -ドメイン認証。
シンプル: JWT トークンのデータ量が少なく、通信速度も非常に速いです。
クロスランゲージ: JWT トークンは JSON 暗号化形式でクライアントに保存されるため、JWT はクロスランゲージであり、あらゆる Web フォームでサポートされています。クロスプラットフォーム: Cookie やセッションに依存せず、サーバーにセッション情報を保存する必要がないため、分散アプリケーションに非常に適しており、拡張にも使用できます。
JWT の最初の部分はヘッダー部分です。 JWT の説明 メタデータの Json オブジェクト。通常は次のようになります。
{ "alg": "HS256", "typ": "JWT" }
alg 属性は署名に使用されるアルゴリズムを示し、デフォルトは HMAC SHA256 (HS256 として記述される)、typ 属性はトークンのタイプを示し、JWT トークンは一律に JWT として記述されます。
JWT の 2 番目の部分はペイロードであり、これも Json オブジェクトです。渡す必要のあるデータが含まれることに加えて、7 つのデフォルト フィールドから選択できます。 iss: 発行者 exp: 有効期限 sub: 件名 aud: ユーザー nbf: 以前は利用不可 iat: リリース時刻 jti: この JWT を識別するために JWT ID が使用されます
{ //默认字段 "sub":"主题123", //自定义字段 "name":"java", "isAdmin":"true", "loginTime":"2021-12-05 12:00:03" }
デフォルトでは、JWT は暗号化されていないことに注意してください。その内容は誰でも解読できるため、機密情報をお持ちの場合は、情報漏洩を防ぐためにここに保存しないでください。 JSON オブジェクトも、Base64 URL アルゴリズムを使用して文字列に変換され、保存されます。
署名ハッシュ部分は、データの上記 2 つの部分に署名することです。base64 でエンコードされたヘッダーとペイロード データを使用し、指定されたアルゴリズムを通じてハッシュを生成して、データは改ざんされません。
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
public class JwtUtil { //创建jwt public static String createJWT(String subject, String issue, Object claim, long ttlMillis) { //当前时间 long nowMillis = System.currentTimeMillis(); //过期时间 long expireMillis = nowMillis + ttlMillis; String result = Jwts.builder() .setSubject(subject) //设置主题 .setIssuer(issue) //发行者 .setId(issue)//jwtID .setExpiration(new Date(expireMillis)) //设置过期日期 .claim("user", claim)//主题,可以包含用户信息 .signWith(getSignatureAlgorithm(), getSignedKey())//加密算法 .compressWith(CompressionCodecs.DEFLATE).compact();//对载荷进行压缩 return result; } // 解析jwt public static Jws<Claims> pareseJWT(String jwt) { Jws<Claims> claims; try { claims = Jwts.parser().setSigningKey(getSignedKey()) .parseClaimsJws(jwt); } catch (Exception ex) { claims = null; } return claims; } //获取主题信息 public static Claims getClaims(String jwt) { Claims claims; try { claims = Jwts.parser().setSigningKey(getSignedKey()) .parseClaimsJws(jwt).getBody(); } catch (Exception ex) { claims = null; } return claims; } } /** * 获取密钥 * * @return Key */ private static Key getSignedKey() { byte[] apiKeySecretBytes = DatatypeConverter .parseBase64Binary(getAuthKey()); Key signingKey = new SecretKeySpec(apiKeySecretBytes, getSignatureAlgorithm().getJcaName()); return signingKey; } private static SignatureAlgorithm getSignatureAlgorithm() { return SignatureAlgorithm.HS256; } //获取密钥,可以动态配置 public static String getAuthKey() { String auth = "123@#1234"; }
Component public class TokenInterceptor extends HandlerInterceptorAdapter { public static Log logger = LogManager.getLogger(TokenInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); logger.info("start TokenInterceptor preHandle.." + uri); //需要过滤特殊请求 if (SystemUtil.isFree(uri) || SystemUtil.isProtected(uri)) { return true; } String metohd=request.getMethod().toString(); logger.info("TokenInterceptor request method:"+metohd); //options 方法需要过滤 if("OPTIONS".equals(metohd)) { return true; } //是否开启token认证 boolean flag = SystemUtil.getVerifyToken(); ResponseResult result = new ResponseResult(); //从请求的head信息中获取token String token = request.getHeader("X-Token"); if (flag) { if(StringUtils.isEmpty(token)) { token=request.getParameter("X-Token"); } // token不存在 if (StringUtils.isEmpty(token)) { result.setCode(ResultCode.NEED_AUTH.getCode()); result.setMsg(ResultCode.NEED_AUTH.getMsg()); WebUtil.writeJson(result, response); return false; } else { Claims claims = JwtUtil.getClaims(token); String subject = ""; if (claims != null) { subject = claims.getSubject(); // 验证主题 if (StringUtils.isEmpty(subject)) { result.setCode(ResultCode.INVALID_TOKEN.getCode()); result.setMsg(ResultCode.INVALID_TOKEN.getMsg()); WebUtil.writeJson(result, response); return false; } } else { result.setCode(ResultCode.INVALID_TOKEN.getCode()); result.setMsg(ResultCode.INVALID_TOKEN.getMsg()); WebUtil.writeJson(result, response); return false; } } } return true; } }
@Configuration public class WebConfig implements WebMvcConfigurer { @Resource private TokenInterceptor tokenInterceptor; public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tokenInterceptor).addPathPatterns("/**"); } }
@RequestMapping("login") public Result login(HttpServletResponse response) { Map<String, Object> map = new HashMap<>(); // Result result = loginAuth(user); int code = result.getCode(); //登录认证成功 if (code ==ResultCode.SUCCESS) { //默认为7天 Long ttlMillis = 7*1000 * 60 * 60 * 24; //过期时间 long expreTime = System.currentTimeMillis() + ttlMillis; String tokenKey = UUID.randomUUID().toString(); String tokenId = JwtUtil.createJWT(user.getUserId(), tokenKey, user.getPassword(), expreTime); map.put("expreTime", expreTime); map.put("tokenId", tokenId); } else { logger.error("login error:" +FastJsonUtil.toJSONString(result)); } return result; }
以上がSpring Boot は JWT をどのように統合してフロントエンドとバックエンドの認証を実現しますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。