共计 10569 个字符,预计需要花费 27 分钟才能阅读完成。
jwt 是什么?
JWTs 是 JSON 对象的编码表示。JSON 对象由零或多个名称 / 值对组成,其中名称为字符串,值为任意 JSON 值。
JWT 有助于在 clear(例如在 URL 中)发送这样的信息,能够被信赖为不可读 (即加密的)、不可批改的(即签名) 和 URL – safe(即 Base64 编码的)。
jwt 的组成
- Header: 题目蕴含了令牌的元数据,并且在最小蕴含签名和 / 或加密算法的类型
- Claims: Claims 蕴含您想要签订的任何信息
- JSON Web Signature (JWS): 在 header 中指定的应用该算法的数字签名和申明
例如:
Header: | |
{ | |
"alg": "HS256", | |
"typ": "JWT" | |
} | |
Claims: | |
{ | |
"sub": "1234567890", | |
"name": "John Doe", | |
"admin": true | |
} | |
Signature: | |
base64UrlEncode(Header) + "." + base64UrlEncode(Claims), |
加密生成的 token:
如何保障 JWT 平安
有很多库能够帮忙您创立和验证 JWT,然而当应用 JWT 时,依然能够做一些事件来限度您的平安危险。在您信赖 JWT 中的任何信息之前,请始终验证签名。这应该是给定的。
换句话说,如果您正在传递一个机密签名密钥到验证签名的办法,并且签名算法被设置为“none”,那么它应该失败验证。
确保签名的机密签名,用于计算和验证签名。机密签名密钥只能由发行者和消费者拜访,不能在这两方之外拜访。
不要在 JWT 中蕴含任何敏感数据。这些令牌通常是用来避免操作 (未加密) 的,因而索赔中的数据能够很容易地解码和读取。
如果您放心重播攻打,包含一个 nonce(jti 索赔)、过期工夫 (exp 索赔) 和创立工夫(iat 索赔)。这些在 JWT 标准中定义得很好。
jwt 的框架:JJWT
JJWT 是一个提供端到端的 JWT 创立和验证的 Java 库。永远收费和开源(Apache License,版本 2.0),JJWT 很容易应用和了解。它被设计成一个以修建为核心的晦涩界面,暗藏了它的大部分复杂性。
- JJWT 的指标是最容易应用和了解用于在 JVM 上创立和验证 JSON Web 令牌 (JWTs) 的库。
- JJWT 是基于 JWT、JWS、JWE、JWK 和 JWA RFC 标准的 Java 实现。
- JJWT 还增加了一些不属于标准的便当扩大,比方 JWT 压缩和索赔强制。
标准兼容:
- 创立和解析明文压缩 JWTs
- 创立、解析和验证所有规范 JWS 算法的数字签名紧凑 JWTs(又称 JWSs):
- HS256: HMAC using SHA-256
- HS384: HMAC using SHA-384
- HS512: HMAC using SHA-512
- RS256: RSASSA-PKCS-v1_5 using SHA-256
- RS384: RSASSA-PKCS-v1_5 using SHA-384
- RS512: RSASSA-PKCS-v1_5 using SHA-512
- PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
- PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384
- PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512
- ES256: ECDSA using P-256 and SHA-256
- ES384: ECDSA using P-384 and SHA-384
- ES512: ECDSA using P-521 and SHA-512
这里以 github 上的 demo 演示,了解原理,集成到本人我的项目中即可。
利用采纳 spring boot + angular + jwt
结构图
Maven 引进 : pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<groupId>com.nibado.example</groupId> | |
<artifactId>jwt-angular-spring</artifactId> | |
<version>0.0.2-SNAPSHOT</version> | |
<properties> | |
<maven.compiler.source>1.8</maven.compiler.source> | |
<maven.compiler.target>1.8</maven.compiler.target> | |
<commons.io.version>2.4</commons.io.version> | |
<jjwt.version>0.6.0</jjwt.version> | |
<junit.version>4.12</junit.version> | |
<spring.boot.version>1.5.3.RELEASE</spring.boot.version> | |
</properties> | |
<build> | |
<plugins> | |
<plugin> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-maven-plugin</artifactId> | |
<version>${spring.boot.version}</version> | |
<executions> | |
<execution> | |
<goals> | |
<goal>repackage</goal> | |
</goals> | |
</execution> | |
</executions> | |
</plugin> | |
</plugins> | |
</build> | |
<dependencies> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-web</artifactId> | |
<version>${spring.boot.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>commons-io</groupId> | |
<artifactId>commons-io</artifactId> | |
<version>${commons.io.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>io.jsonwebtoken</groupId> | |
<artifactId>jjwt</artifactId> | |
<version>${jjwt.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>junit</groupId> | |
<artifactId>junit</artifactId> | |
<version>${junit.version}</version> | |
</dependency> | |
</dependencies> | |
</project> |
WebApplication.java
package com.nibado.example.jwtangspr; | |
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; | |
import org.springframework.boot.web.servlet.FilterRegistrationBean; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.ComponentScan; | |
import org.springframework.context.annotation.Configuration; | |
@EnableAutoConfiguration | |
@ComponentScan | |
@Configuration | |
public class WebApplication { | |
// 过滤器 | |
@Bean | |
public FilterRegistrationBean jwtFilter() {final FilterRegistrationBean registrationBean = new FilterRegistrationBean(); | |
registrationBean.setFilter(new JwtFilter()); | |
registrationBean.addUrlPatterns("/api/*"); | |
return registrationBean; | |
} | |
public static void main(final String[] args) throws Exception {SpringApplication.run(WebApplication.class, args); | |
} | |
} |
JwtFilter.java
package com.nibado.example.jwtangspr; | |
import java.io.IOException; | |
import javax.servlet.FilterChain; | |
import javax.servlet.ServletException; | |
import javax.servlet.ServletRequest; | |
import javax.servlet.ServletResponse; | |
import javax.servlet.http.HttpServletRequest; | |
import org.springframework.web.filter.GenericFilterBean; | |
import io.jsonwebtoken.Claims; | |
import io.jsonwebtoken.Jwts; | |
import io.jsonwebtoken.SignatureException; | |
public class JwtFilter extends GenericFilterBean { | |
@Override | |
public void doFilter(final ServletRequest req, | |
final ServletResponse res, | |
final FilterChain chain) throws IOException, ServletException {final HttpServletRequest request = (HttpServletRequest) req; | |
// 客户端将 token 封装在申请头中,格局为(Bearer 后加空格):Authorization:Bearer +token | |
final String authHeader = request.getHeader("Authorization"); | |
if (authHeader == null || !authHeader.startsWith("Bearer")) {throw new ServletException("Missing or invalid Authorization header."); | |
} | |
// 去除 Bearer 后局部 | |
final String token = authHeader.substring(7); | |
try { | |
// 解密 token,拿到外面的对象 claims | |
final Claims claims = Jwts.parser().setSigningKey("secretkey") | |
.parseClaimsJws(token).getBody(); | |
// 将对象传递给下一个申请 | |
request.setAttribute("claims", claims); | |
} | |
catch (final SignatureException e) {throw new ServletException("Invalid token."); | |
} | |
chain.doFilter(req, res); | |
} | |
} |
UserController.java
package com.nibado.example.jwtangspr; | |
import java.util.Arrays; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import javax.servlet.ServletException; | |
import org.springframework.web.bind.annotation.RequestBody; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.RequestMethod; | |
import org.springframework.web.bind.annotation.RestController; | |
import io.jsonwebtoken.Jwts; | |
import io.jsonwebtoken.SignatureAlgorithm; | |
@RestController | |
@RequestMapping("/user") | |
public class UserController { | |
// 这里模仿数据库 | |
private final Map<String, List<String>> userDb = new HashMap<>(); | |
@SuppressWarnings("unused") | |
private static class UserLogin { | |
public String name; | |
public String password; | |
} | |
public UserController() {userDb.put("tom", Arrays.asList("user")); | |
userDb.put("wen", Arrays.asList("user", "admin")); | |
} | |
/* 以上是模仿数据库,并往数据库插入 tom 和 sally 两条记录 */ | |
@RequestMapping(value = "login", method = RequestMethod.POST) | |
public LoginResponse login(@RequestBody final UserLogin login) | |
throws ServletException {if (login.name == null || !userDb.containsKey(login.name)) {throw new ServletException("Invalid login"); | |
} | |
// 加密生成 token | |
return new LoginResponse(Jwts.builder().setSubject(login.name) | |
.claim("roles", userDb.get(login.name)).setIssuedAt(new Date()) | |
.signWith(SignatureAlgorithm.HS256, "secretkey").compact()); | |
} | |
@SuppressWarnings("unused") | |
private static class LoginResponse { | |
public String token; | |
public LoginResponse(final String token) {this.token = token;} | |
} | |
} |
ApiController.java
package com.nibado.example.jwtangspr; | |
import io.jsonwebtoken.Claims; | |
import java.util.List; | |
import javax.servlet.ServletException; | |
import javax.servlet.http.HttpServletRequest; | |
import org.springframework.web.bind.annotation.PathVariable; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
import org.springframework.web.bind.annotation.RequestMethod; | |
import org.springframework.web.bind.annotation.RestController; | |
@RestController | |
@RequestMapping("/api") | |
public class ApiController {@SuppressWarnings("unchecked") | |
@RequestMapping(value = "role/{role}", method = RequestMethod.GET) | |
public Boolean login(@PathVariable final String role, | |
final HttpServletRequest request) throws ServletException {final Claims claims = (Claims) request.getAttribute("claims"); | |
return ((List<String>) claims.get("roles")).contains(role); | |
} | |
} |
index.html
<!doctype html> | |
<html ng-app="myApp"> | |
<head> | |
<meta charset="utf-8"/> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> | |
<title>JSON Web Token / AngularJS / Spring Boot example</title> | |
<meta name="description" content=""> | |
<meta name="viewport" content="width=device-width"> | |
<link rel="stylesheet" href="libs/bootstrap/css/bootstrap.css"> | |
<script src="libs/jquery/jquery.js"></script> | |
<script src="libs/bootstrap/js/bootstrap.js"></script> | |
<script src="libs/angular/angular.js"></script> | |
<script src="app.js"></script> | |
</head> | |
<body> | |
<div class="container" ng-controller='MainCtrl'> | |
<h1>{{greeting}}</h1> | |
<div ng-show="!loggedIn()"> | |
Please log in (tom and sally are valid names)</br> | |
<form ng-submit="login()"> | |
Username: <input type="text" ng-model="userName"/><span><input type="submit" value="Login"/> | |
</form> | |
</div> | |
<div class="alert alert-danger" role="alert" ng-show="error.data.message"> | |
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> | |
<span class="sr-only">Error:</span> | |
{{error.data.message}} | |
</div> | |
<div ng-show="loggedIn()"> | |
<div class="row"> | |
<div class="col-md-6"> | |
<h3><span class="label label-success">Success!</span> Welcome {{userName}}</h3> | |
<a href ng-click="logout()">(logout)</a> | |
</div> | |
<div class="col-md-4"> | |
<div class="row header"> | |
<div class="col-sm-4">{{userName}} is a</div> | |
</div> | |
<div class="row"> | |
<div class="col-sm-2">User</div> | |
<div class="col-sm-2"><span class="glyphicon glyphicon-ok" aria-hidden="true" ng-show="roleUser"></span></div> | |
</div> | |
<div class="row"> | |
<div class="col-sm-2">Admin</div> | |
<div class="col-sm-2"><span class="glyphicon glyphicon-ok" aria-hidden="true" ng-show="roleAdmin"></span></div> | |
</div> | |
<div class="row"> | |
<div class="col-sm-2">Foo</div> | |
<div class="col-sm-2"><span class="glyphicon glyphicon-ok" aria-hidden="true" ng-show="roleFoo"></span></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</body> | |
</html> |
app.js
var appModule = angular.module('myApp', []); | |
appModule.controller('MainCtrl', ['mainService','$scope','$http', | |
function(mainService, $scope, $http) { | |
$scope.greeting = 'Welcome to the JSON Web Token / AngularJR / Spring example!'; | |
$scope.token = null; | |
$scope.error = null; | |
$scope.roleUser = false; | |
$scope.roleAdmin = false; | |
$scope.roleFoo = false; | |
$scope.login = function() { | |
$scope.error = null; | |
mainService.login($scope.userName).then(function(token) { | |
$scope.token = token; | |
$http.defaults.headers.common.Authorization = 'Bearer' + token; | |
$scope.checkRoles();}, | |
function(error){ | |
$scope.error = error | |
$scope.userName = ''; | |
}); | |
} | |
$scope.checkRoles = function() {mainService.hasRole('user').then(function(user) {$scope.roleUser = user}); | |
mainService.hasRole('admin').then(function(admin) {$scope.roleAdmin = admin}); | |
mainService.hasRole('foo').then(function(foo) {$scope.roleFoo = foo}); | |
} | |
$scope.logout = function() { | |
$scope.userName = ''; | |
$scope.token = null; | |
$http.defaults.headers.common.Authorization = ''; | |
} | |
$scope.loggedIn = function() {return $scope.token !== null;} | |
} ]); | |
appModule.service('mainService', function($http) { | |
return {login : function(username) {return $http.post('/user/login', {name: username}).then(function(response) {return response.data.token;}); | |
}, | |
hasRole : function(role) {return $http.get('/api/role/' + role).then(function(response){console.log(response); | |
return response.data; | |
}); | |
} | |
}; | |
}); |
运行利用
成果
原文链接:https://blog.csdn.net/change_…
版权申明:本文为 CSDN 博主「J_小浩子」的原创文章,遵循 CC 4.0 BY-SA 版权协定,转载请附上原文出处链接及本申明。
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿(2021 最新版)
2. 别在再满屏的 if/ else 了,试试策略模式,真香!!
3. 卧槽!Java 中的 xx ≠ null 是什么新语法?
4.Spring Boot 2.5 重磅公布,光明模式太炸了!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!