基于Token的身份验证--JWT

初次了解JWT,很基础,高手勿喷。

基于Token的身份验证用来替代传统的cookie+session身份验证方法中的session。

JWT是啥?

JWT就是一个字符串,经过加密处理与校验处理的字符串,形式为

    "A.B.C"

A由JWT头部信息header加密得到
B由JWT用到的身份验证信息json数据加密得到
C由A和B加密得到,是校验部分

怎样生成A?

header格式为:

{
    "typ": "JWT",
    "alg": "HS256" 
}

它就是一个json串,两个字段是必须的,不能多也不能少。alg字段指定了生成C的算法,默认值是HS256
将header用base64加密,得到A
通常,JWT库中,可以把A部分固定写死,用户最多指定一个alg的取值

怎样计算B?

根据JWT claim set[用base64]加密得到的。claim set是一个json数据,是表明用户身份的数据,可自行指定字段很灵活,也有固定字段表示特定含义(但不一定要包含特定字段,只是推荐)。
这里偷懒,直接用php中的代码来表示claim set了,重在说明字段含义:

$token = array(
    "iss" => "http://example.org",   #非必须。issuer 请求实体,可以是发起请求的用户的信息,也可是jwt的签发者。
    "iat" => 1356999524, #非必须。issued at。 token创建时间,unix时间戳格式 "exp" => "1548333419", #非必须。expire 指定token的生命周期。unix时间戳格式 "aud" => "http://example.com", #非必须。接收该JWT的一方。 "sub" => "[email protected]", #非必须。该JWT所面向的用户 "nbf" => 1357000000, # 非必须。not before。如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟。 "jti" => '222we', # 非必须。JWT ID。针对当前token的唯一标识 "GivenName" => "Jonny", # 自定义字段 "Surname" => "Rocket", # 自定义字段 "Email" => "[email protected]", # 自定义字段 "Role" => ["Manager", "Project Administrator"] # 自定义字段 );

JWT遵循RFC7519,里面提到claim set的json数据中,自定义字段的key是一个string,value是一个json数据。因此随意编写吧,很灵活。

个人初学,认为一个最基本最简单最常用的claim set为:

$token=array(
    "user_id" => 123456, #用户id,表明用户
    "iat" => 1356999524, #token发布时间 "exp" => 1556999524, #token过期时间 );

将claim set加密后得到B,学名payload

怎样计算C?

A.B使用HS256加密(其实是用header中指定的算法),当然加密过程中还需要密钥(自行指定的一个字符串)。
加密得到C,学名signature,其实就是一个字符串。作用类似于CRC校验,保证加密没有问题。

好了,现在A.B.C就是生成的token了。

怎样使用token?

可以放到HTTP请求的请求头中,通常是Authorization字段。
也有人说放到cookie。不过移动端app用cookie似乎不方便。

token应用流程?

    1. 初次登录:用户初次登录,输入用户名密码
    2. 密码验证:服务器从数据库取出用户名和密码进行验证
    3. 生成JWT:服务器端验证通过,根据从数据库返回的信息,以及预设规则,生成JWT
    4. 返还JWT:服务器的HTTP RESPONSE中将JWT返还
    5. 带JWT的请求:以后客户端发起请求,HTTP REQUEST HEADER中的Authorizatio字段都要有值,为JWT
 
使用样例:
 1 package com.feihe.util;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 import com.auth0.jwt.JWTSigner;
 6 import com.auth0.jwt.JWTVerifier;
 7 import com.auth0.jwt.internal.com.fasterxml.jackson.databind.ObjectMapper;
 8 import com.feihe.util.ConfHelper;
 9  
10 public class JWT {
11  
12     private static final String SECRET = "XX#$%()(#*!()!KL<><MQLMNQNQJQK sdfkjsdrow32234545fdf>?N<:{LWPW";   // 密码
13     
14     private static final String EXP = "exp";
15     
16     private static final String PAYLOAD = "payload";
17  
18     /**
19      * get jwt String of object 
20      * 加密,传入一个对象和有效期
21      * @param object the POJO object
22      * @param maxAge the milliseconds of life time
23      * @return the jwt token
24      */
25     public static <T> String sign(T object) {
26         try {
27             final JWTSigner signer = new JWTSigner(SECRET);
28             final Map<String, Object> claims = new HashMap<String, Object>();
29             ObjectMapper mapper = new ObjectMapper();
30             String jsonString = mapper.writeValueAsString(object);   // 数据部分
31             claims.put(PAYLOAD, jsonString);
32             String jwtExp = ConfHelper.getConf("JWT_EXP");  // 设置token有效时长 暂定30分钟
33             Long maxAge = Long.parseLong(jwtExp);
34             claims.put(EXP, System.currentTimeMillis() + maxAge);
35             return signer.sign(claims);
36         } catch(Exception e) {
37             return null;
38         }
39     }
40     
41     
42     /**
43      * get the object of jwt if not expired  
44      *  解密,传入一个加密后的token字符串和解密后的类型
45      * @param jwt
46      * @return POJO object
47      */
48     public static<T> T unsign(String jwt, Class<T> classT) {
49         final JWTVerifier verifier = new JWTVerifier(SECRET);
50         try {
51             final Map<String,Object> claims= verifier.verify(jwt);
52             if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
53                 long exp = (Long)claims.get(EXP);
54                 long currentTimeMillis = System.currentTimeMillis();
55                 if (exp > currentTimeMillis) {    // 验证token是否过期
56                     String json = (String)claims.get(PAYLOAD);
57                     ObjectMapper objectMapper = new ObjectMapper();
58                     return objectMapper.readValue(json, classT);
59                 }
60             }
61             return null;
62         } catch (Exception e) {
63             return null;
64         }
65     }
66     
67 }

当用户登录成功后将用户信息或想要放在token值的信息调用JWT.sign()进行处理

   

1 String token = JWT.sign(user);   // 登录成功,进行JWT处理

当验证登陆时将token值进行解密即可

1 SysUser user = JWT.unsign(token, SysUser.class);

根据结果判断登录情况。

ps:最后额外附上Spring拦截的配置

 1     <mvc:interceptors>    
 2         <mvc:interceptor>    
 3             <!-- 匹配的是url路径, 如果不配置或/**,将拦截所有的Controller -->  
 4             <mvc:mapping path="/**" />  
 5             <!-- /register 注册 和 /login 登陆 不需要拦截-->  
 6             <!-- <mvc:exclude-mapping path="/user/register" /> -->
 7             <mvc:exclude-mapping path="/user/login" />
 8             
 9             <bean class="com.feihe.filter.TokenInterceptor"></bean>    
10         </mvc:interceptor>  
11         <!-- 当设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法 -->  
12     </mvc:interceptors> 
 1 package com.feihe.filter;
 2 
 3 import java.io.PrintWriter;
 4 import java.util.List;
 5 
 6 import javax.servlet.http.HttpServletRequest;
 7 import javax.servlet.http.HttpServletResponse;
 8 
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.web.servlet.HandlerInterceptor;
11 import org.springframework.web.servlet.ModelAndView;
12 
13 import com.feihe.coa.bean.SysLogin;
14 import com.feihe.coa.bean.SysUser;
15 import com.feihe.coa.mapper.SysLoginMapper;
16 import com.feihe.common.ResponseData;
17 import com.feihe.util.JWT;
18 
19 import net.sf.json.JSONObject;
20 
21 public class TokenInterceptor implements HandlerInterceptor{
22 
23     @Autowired
24     private SysLoginMapper sysLoginMapper;
25     
26     public void afterCompletion(HttpServletRequest request,
27             HttpServletResponse response, Object handler, Exception arg3)
28             throws Exception {
29     }
30 
31     public void postHandle(HttpServletRequest request, HttpServletResponse response,
32             Object handler, ModelAndView model) throws Exception {
33     }
34 
35     //拦截每个请求
36     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
37         response.setCharacterEncoding("utf-8");
38         String token = request.getParameter("token");
39         ResponseData responseData;
40         //token不存在
41         if(null != token) {
42             SysUser user = JWT.unsign(token, SysUser.class);
43             //解密token后的user是否为null,一般都是token过期
44             if(null != user) {
45                 if(user.getUserId() != 0) {
46                     SysLogin login = new SysLogin();
47                     login.setUserId(user.getUserId());
48                     login.setState(1);  // 登入状态为1
49                     // 判断是否存在该用户的“登入”状态
50                     List<SysLogin> loginList = sysLoginMapper.selectByUidAndState(login);
51                     if(null !=loginList && loginList.size() > 0){
52                         if(token.equals(loginList.get(0).getToken())){
53                             return true;
54                         }else {
55                             responseData = ResponseData.forbidden();
56                             responseMessage(response, response.getWriter(), responseData);
57                             return false;
58                         }
59                     }else {
60                         responseData = ResponseData.forbidden();
61                         responseMessage(response, response.getWriter(), responseData);
62                         return false;
63                     }
64                 }else {
65                     responseData = ResponseData.forbidden();
66                     responseMessage(response, response.getWriter(), responseData);
67                     return false;
68                 }
69             }else {
70                 responseData = ResponseData.forbidden();
71                 responseMessage(response, response.getWriter(), responseData);
72                 return false;
73             }
74         }else {
75             responseData = ResponseData.forbidden();
76             responseMessage(response, response.getWriter(), responseData);
77             return false;
78         }
79     }
80 
81     //请求不通过,返回错误信息给客户端
82     private void responseMessage(HttpServletResponse response, PrintWriter out, ResponseData responseData) {
83         responseData = ResponseData.forbidden();
84         response.setContentType("application/json; charset=utf-8");  
85         String json = JSONObject.fromObject(responseData).toString();
86         out.print(json);
87         out.flush();
88         out.close();
89     }
90 
91 }

基于Token的身份验证用来替代传统的cookie+session身份验证方法中的session。

JWT是啥?

JWT就是一个字符串,经过加密处理与校验处理的字符串,形式为

    "A.B.C"

A由JWT头部信息header加密得到
B由JWT用到的身份验证信息json数据加密得到
C由A和B加密得到,是校验部分

怎样生成A?

header格式为:

{
    "typ": "JWT",
    "alg": "HS256" 
}

它就是一个json串,两个字段是必须的,不能多也不能少。alg字段指定了生成C的算法,默认值是HS256
将header用base64加密,得到A
通常,JWT库中,可以把A部分固定写死,用户最多指定一个alg的取值

怎样计算B?

根据JWT claim set[用base64]加密得到的。claim set是一个json数据,是表明用户身份的数据,可自行指定字段很灵活,也有固定字段表示特定含义(但不一定要包含特定字段,只是推荐)。
这里偷懒,直接用php中的代码来表示claim set了,重在说明字段含义:

$token = array(
    "iss" => "http://example.org",   #非必须。issuer 请求实体,可以是发起请求的用户的信息,也可是jwt的签发者。
    "iat" => 1356999524, #非必须。issued at。 token创建时间,unix时间戳格式 "exp" => "1548333419", #非必须。expire 指定token的生命周期。unix时间戳格式 "aud" => "http://example.com", #非必须。接收该JWT的一方。 "sub" => "[email protected]", #非必须。该JWT所面向的用户 "nbf" => 1357000000, # 非必须。not before。如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟。 "jti" => '222we', # 非必须。JWT ID。针对当前token的唯一标识 "GivenName" => "Jonny", # 自定义字段 "Surname" => "Rocket", # 自定义字段 "Email" => "[email protected]", # 自定义字段 "Role" => ["Manager", "Project Administrator"] # 自定义字段 );

JWT遵循RFC7519,里面提到claim set的json数据中,自定义字段的key是一个string,value是一个json数据。因此随意编写吧,很灵活。

个人初学,认为一个最基本最简单最常用的claim set为:

$token=array(
    "user_id" => 123456, #用户id,表明用户
    "iat" => 1356999524, #token发布时间 "exp" => 1556999524, #token过期时间 );

将claim set加密后得到B,学名payload

怎样计算C?

A.B使用HS256加密(其实是用header中指定的算法),当然加密过程中还需要密钥(自行指定的一个字符串)。
加密得到C,学名signature,其实就是一个字符串。作用类似于CRC校验,保证加密没有问题。

好了,现在A.B.C就是生成的token了。

怎样使用token?

可以放到HTTP请求的请求头中,通常是Authorization字段。
也有人说放到cookie。不过移动端app用cookie似乎不方便。

token应用流程?

    1. 初次登录:用户初次登录,输入用户名密码
    2. 密码验证:服务器从数据库取出用户名和密码进行验证
    3. 生成JWT:服务器端验证通过,根据从数据库返回的信息,以及预设规则,生成JWT
    4. 返还JWT:服务器的HTTP RESPONSE中将JWT返还
    5. 带JWT的请求:以后客户端发起请求,HTTP REQUEST HEADER中的Authorizatio字段都要有值,为JWT

猜你喜欢

转载自www.cnblogs.com/haw2106/p/9956497.html