目录
[1. JWT简述](#1. JWT简述)
[1.1 什么是JWT](#1.1 什么是JWT)
[1.2 为什么使用JWT](#1.2 为什么使用JWT)
[1.3 JWT结构](#1.3 JWT结构)
[1.4 验证过程](#1.4 验证过程)
[2. JWT示例](#2. JWT示例)
[2.1 后台程序](#2.1 后台程序)
[2.2 前台加入jwt令牌功能](#2.2 前台加入jwt令牌功能)
1. JWT简述
1.1 什么是JWT
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案
1.2 为什么使用JWT
传统的intenet服务的认证是通过session进行的,当用户通过了安全认证后,则在服务端的session对象中保存该用户的认证信息,这样该用户对服务的访问被认为是安全的。这种模式的最大问题是没有分布式架构,不方便进行横向扩展,这种模式只适合于单体应用模式。如果需要进行服务集群则需要处理好共享session的问题。 如果一个庞大的系统需要按服务分解为多个独立的服务,使用分布式架构,则这种方式更难处理。使用jwt可以方便的处理上面提到的问题。
1.3 JWT结构
JWT头 :
JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。{"alg": "HS256","typ": "JWT"}。
有效载荷 :
有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。这些声明被JWT标准称为claims。
例如:{"sub":"123","name":"Tom","admin":true},sub代表这个token的所有人,存储的是所有人的ID;name表示这个所有人的名字;admin表示所有人是否管理员的角色。当后面对JWT进行验证的时候,这些claim都能发挥特定的作用。
根据JWT的标准,这些claims可以分为以下三种类型:
A. Reserved claims(保留)
它的含义就像是编程语言的保留字一样,属于JWT标准里面规定的一些claim。JWT标准里面定义好的claim有:
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
B. Public claims,略(不重要)
C. Private claims(私有)
这个指的就是自定义的claim,比如前面那个示例中的admin和name都属于自定的claim。这些claim跟JWT标准规定的claim区别在于:JWT规定的claim,JWT的接收方在拿到JWT之后,都知道怎么对这些标准的claim进行验证;而private claims不会验证,除非明确告诉接收方要对这些claim进行验证以及规则才行。
按照JWT标准的说明:保留的claims都是可选的,在生成payload不强制用上面的那些claim,你可以完全按照自己的想法来定义payload的结构,不过这样搞根本没必要:第一是,如果把JWT用于认证, 那么JWT标准内规定的几个claim就足够用了,甚至只需要其中一两个就可以了,假如想往JWT里多存一些用户业务信息,比如角色和用户名等,这倒是用自定义的claim来添加;第二是,JWT标准里面针对它自己规定的claim都提供了有详细的验证规则描述,每个实现库都会参照这个描述来提供JWT的验证实现,所以如果是自定义的claim名称,那么你用到的实现库就不会主动去验证这些claim。
signature :
签名的作用是防篡改。签名是把header和payload对应的json结构进行base64url编码之后得到的两个串用英文句点号拼接起来,然后根据header里面alg指定的签名算法生成出来的。算法不同,签名结果不同。以alg: HS256为例来说明前面的签名如何来得到。照前面alg可用值的说明,HS256其实包含的是两种算法:HMAC算法和SHA256算法,前者用于生成摘要,后者用于对摘要进行数字签名。这两个算法也可以用HMACSHA256来统称
JWT实际结构示例:
eyJhbGciOiJIUzI1NiJ9. eyJzdWIiOiJ7fSIsImlzcyI6InpraW5nIiwiZXhwIjoxNTYyODUwMjM3LCJpYXQiOjE1NjI4NDg0MzcsImp0aSI6ImM5OWEyMzRmMDc4NzQyZWE4YjlmYThlYmYzY2VhNjBlIiwidXNlcm5hbWUiOiJ6c3MifQ. WUfqhFTeGzUZCpCfz5eeEpBXBZ8-lYg1htp-t7wD3I4
1.4 验证过程
客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中,此后在于服务器的交互中都携带者JWT信息,它验证的方法其实很简单,只要把header做base64url解码,就能知道JWT用的什么算法做的签名,然后用这个算法,再次用同样的逻辑对header和payload做一次签名,并比较这个签名是否与JWT本身包含的第三个部分的串是否完全相同,只要不同,就可以认为这个JWT是一个被篡改过的串。
2. JWT示例
2.1 后台程序
1)将资料中src目录下的代码(CorsFilter.java,JwtFilter.java,JwtUtils.java)拷入项目的util目录下(可以根据项目设置的目录结构调整)。
2)程序的解释详见代码中的注释
3)在web.xml中配置JwtFilter过滤器
4)在用户登录验证成功后,需要为该用户生成jwt令牌,具体实现可参考UserAction.java
5)测试后台的验证服务,确定在验证通过的前提下可以正确的生成jwt令牌。
2.2 前台加入jwt令牌功能
1) 在store/state.js中加入jwt变量
//存放全局参数的容器,组件可以通过state.js获取全局参数
const state = {
LeftAsideState: 'open',
PersonName:'张飞',
//存放jwt令牌
jwt:null
}
export default state
2)在store/mutations.js中加入设置jwt方法
//设置JWT令牌
setJwtToken: (state, payload) => {
state.jwt = payload.jwt;
}
3)在store/getters.js中加入获取jwt的方法
//获取jwt令牌
getJwtToken: function(state) {
return state.jwt;
}
4)修改main.js应用入口
//将vue实例赋给window.vm属性,以便于在http.js的响应拦截器和请求拦截器中获取vue实例
window.vm = new Vue({
el: '#app',
router,
store,
components: {App},
template: '<App/>'
})
5)完善api/http.js中的请求拦截器和响应拦截器
// 请求拦截器
axios.interceptors.request.use(function(config) {
//获取store中的jwt令牌,如果正常获取,将其放入请求头,
//在调用后台服务时,服务会通过过滤器验证jwt令牌的有效性
//如果没有令牌或令牌无效则拒绝服务。
let jwt = window.vm.$store.getters.getJwtToken;
if(jwt) {
config.headers['jwt'] = jwt;
}
return config;
}, function(error) {
return Promise.reject(error);
});
// 响应拦截器
axios.interceptors.response.use(function(response) {
//获取响应头中的jwt令牌数据(认证服务将生成的令牌放入header的jwt属性中)
//如果获取到jwt令牌,在保存到store中,以后发送请求时需要将令牌放入请求头
let jwt = response.headers['jwt'];
if(jwt) {
window.vm.$store.commit('setJwtToken',{jwt:jwt});
}
return response;
}, function(error) {
return Promise.reject(error);
});
完毕,测试