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
???? 4.0.0 ????com.nibado.example ????jwt-angular-spring ????0.0.2-SNAPSHOT ???????????? ????1.8 ????????1.8 ????????2.4 ????????0.6.0 ????????4.12 ????????1.5.3.RELEASE ???????????? ???????????????? ???????????????????? ????????org.springframework.boot ????????????????spring-boot-maven-plugin ????????????????${spring.boot.version} ???????????????????????????????????? ???????????????????????????????????? ???????????????????????????????????????????? ????????????????????repackage ???????????????????????????????? ???????????? ????????org.springframework.boot ????????????spring-boot-starter-web ????????????${spring.boot.version} ???????????????????? ????????commons-io ????????????commons-io ????????????${commons.io.version} ???????????????????? ????????io.jsonwebtoken ????????????jjwt ????????????${jjwt.version} ???????????????????? ????junit ????????????junit ????????????${junit.version} ????????
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>?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)?claims.get("roles")).contains(role); ?} }
index.html
???? ???? ????JSON?Web?Token?/?AngularJS?/?Spring?Boot?example ???? ???? ???? ???? ???? ???? ?????{{greeting}}
???Please?log?in?(tom?and?sally?are?valid?names) ?? ????? ???Error: ???{{error.data.message}} ?? ??????????????Success!?Welcome?{{userName}}
?????(logout) ?????????????{{userName}}?is?a???????User????????? ??Admin???????????? ???Foo?????
/images/chaijie_default.png
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; ????????????}); ????????} ????}; });
?
?
运行应用
编辑:黄飞
?
评论