Jwt介绍
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT的构成
- 第一部分我们称它为头部(header),通过base64加密
- 第二部分我们称其为载荷(payload,类似于飞机上承载的物品),通过base64加密
- 第三部分是签证(signature)
header
token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。例如:
{
'typ': 'JWT',
'alg': 'HS256'
}
payload
- Payload JWT的第二部分是payload,它包含声明(要求)。
- 声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public 和 private。
- Registered claims :这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
- Public claims : 可以随意定义。 Private claims :用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。 下面是一个例子
{
"sub": '1234567890',
"name": 'john',
"admin":true
}
注意,不要在JWT的payload或header中放置敏感信息,除非它们是加密的。
signature
Header 和 Payload 编码后的字符串拼接后再用HS256签名算法(Header中alg指明的算法)加密,在加密的过程中还需加上secret(密钥),最后得到签名
如何生成JWT密钥
jwt官网上有相关的依赖,也可以通过在官网上验证密钥的信息,相关依赖如下
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
/**
* 生成Jwt
*/
@Test
public void testJwt() {
long date = System.currentTimeMillis() + 60 * 1000;// 当前时间(毫秒)增加60秒
Date date1=new Date(date); // 获得时间
String rose = Jwts.builder()
.setId("888") // 设置JWTId
.setSubject("Rose") // 设置标题
.setExpiration(date1)// 设置当前token过期时间
.setIssuedAt(new Date())// 设置当前token创建时间
.claim("name","jack") // 自定义参数设置
.claim("logo","img.jpg")// 自定义参数设置
.signWith(SignatureAlgorithm.HS256, "112233").compact();// 设置密钥和密钥的加密方式
System.out.println("rose = " + rose);
}
/**
* 解析token,解析token时需要之前设置的singKey密钥才能进行解析
*/
@Test
public void ParseToken(){
String token="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiJSb3NlIiwiZXhwIjoxNjIyNTM4MDY5LCJpYXQiOjE2MjI1MzgwMDksIm5hbWUiOiJqYWNrIiwibG9nbyI6ImltZy5qcGcifQ.VAzLBSShT1kxET67QCrxdrwqQ5Eyz42mBsY4Vb1HuCY";
Claims claims = (Claims) Jwts.parser().setSigningKey("112233").parse(token).getBody();
String id = claims.getId();
System.out.println("id = " + id);
String subject = claims.getSubject();
System.out.println("subject = " + subject);
Date issuedAt = claims.getIssuedAt();
System.out.println("issuedAt = " + issuedAt);
}
相关的JWT工具类可以查看JAVA——JWT帮助类
在密码模式中整合JWT使用
本篇文章使用到了上一章的代码SpringBoot+SpringSecurity+SpringCloudOauth2密码模式使用(二),可以参考上一章是如何实现密码模式的,创建一个
MyJwtTokenStore
配置类
@Configuration
public class MyJwtTokenStore {
/**
* 储存token的Store
* @return
*/
@Bean
public TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* 这个bean将自动生成的token字符转为jwt格式的token
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter=new JwtAccessTokenConverter();
// 设置当前Jwt的密钥
jwtAccessTokenConverter.setSigningKey("test_key");
return jwtAccessTokenConverter;
}
}
在授权服务器里加入之前注入好前Bean
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Lazy
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Lazy
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Lazy
@Autowired
private UserService userService;
@Lazy
@Autowired
private TokenStore jwtTokenStore;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 设置相关
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userService)
.tokenStore(jwtTokenStore)// 储存Token的Store
.accessTokenConverter(jwtAccessTokenConverter)// 将token转为Jwt格式
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
// 授权服务的名字
.withClient("clients")
// 授权码
.secret(passwordEncoder.encode("112233"))
// 重定向位置
.redirectUris("http://www.baidu.com")
// 授权范围
.scopes("all")
/**
* authorization_code 授权码模式
* password 密码模式
*
*/
.authorizedGrantTypes("authorization_code","password");
}
}
启动项目
这里也是使用postMan进行测试,观察以下获取到的·access_token字符串,将他拿到jwt官网上进行测试观察
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjMyMjc0ODQsInN5cyI6InpwaiIsImF1dGhvcml0aWVzIjpbImFkbWluIl0sImp0aSI6IjFmOWRmYzkxLWExZmUtNGRmZi05Zjk2LTY5Y2QwODFlOGQ2YiIsImNsaWVudF9pZCI6ImNsaWVudHMifQ.ZMfbt3kvn1A2wdHIbRyGlnE_f9I_cDh1RytxY9Ce_sA
从官网的对比来看是没有问题的,接下来就可以通过对比当前token是否有效来判断有没有访问当前资源的权利了
通过判断请求头上是否带有token并且token正确
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/getCurrentUser")
public Object getCurrentUser(Authentication authentication, HttpServletRequest request){
String header = request.getHeader("Authorization");
String token = header.substring(header.lastIndexOf("bearer") + 7);
return Jwts.parser().setSigningKey("test_key".getBytes(StandardCharsets.UTF_8))
.parse(token).getBody();
}
}
bearer后面必须要有空格,博主使用的是正规一点的token设置方式,可以不用和我一样,如果一样就打一个空格,这样就可以获取到相关的资源了