初次了解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" => "jrocket@example.com", #非必须。该JWT所面向的用户
"nbf" => 1357000000, # 非必须。not before。如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟。
"jti" => '222we', # 非必须。JWT ID。针对当前token的唯一标识 "GivenName" => "Jonny", # 自定义字段
"Surname" => "Rocket", # 自定义字段
"Email" => "jrocket@example.com", # 自定义字段
"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
 
使用样例:
 package com.feihe.util;

 import java.util.HashMap;
import java.util.Map;
import com.auth0.jwt.JWTSigner;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.internal.com.fasterxml.jackson.databind.ObjectMapper;
import com.feihe.util.ConfHelper; public class JWT { private static final String SECRET = "XX#$%()(#*!()!KL<><MQLMNQNQJQK sdfkjsdrow32234545fdf>?N<:{LWPW"; // 密码 private static final String EXP = "exp"; private static final String PAYLOAD = "payload"; /**
* get jwt String of object
* 加密,传入一个对象和有效期
* @param object the POJO object
* @param maxAge the milliseconds of life time
* @return the jwt token
*/
public static <T> String sign(T object) {
try {
final JWTSigner signer = new JWTSigner(SECRET);
final Map<String, Object> claims = new HashMap<String, Object>();
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(object); // 数据部分
claims.put(PAYLOAD, jsonString);
String jwtExp = ConfHelper.getConf("JWT_EXP"); // 设置token有效时长 暂定30分钟
Long maxAge = Long.parseLong(jwtExp);
claims.put(EXP, System.currentTimeMillis() + maxAge);
return signer.sign(claims);
} catch(Exception e) {
return null;
}
} /**
* get the object of jwt if not expired
* 解密,传入一个加密后的token字符串和解密后的类型
* @param jwt
* @return POJO object
*/
public static<T> T unsign(String jwt, Class<T> classT) {
final JWTVerifier verifier = new JWTVerifier(SECRET);
try {
final Map<String,Object> claims= verifier.verify(jwt);
if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
long exp = (Long)claims.get(EXP);
long currentTimeMillis = System.currentTimeMillis();
if (exp > currentTimeMillis) { // 验证token是否过期
String json = (String)claims.get(PAYLOAD);
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, classT);
}
}
return null;
} catch (Exception e) {
return null;
}
} }

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

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

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

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

根据结果判断登录情况。

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

     <mvc:interceptors>
<mvc:interceptor>
<!-- 匹配的是url路径, 如果不配置或/**,将拦截所有的Controller -->
<mvc:mapping path="/**" />
<!-- /register 注册 和 /login 登陆 不需要拦截-->
<!-- <mvc:exclude-mapping path="/user/register" /> -->
<mvc:exclude-mapping path="/user/login" /> <bean class="com.feihe.filter.TokenInterceptor"></bean>
</mvc:interceptor>
<!-- 当设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法 -->
</mvc:interceptors>
 package com.feihe.filter;

 import java.io.PrintWriter;
import java.util.List; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import com.feihe.coa.bean.SysLogin;
import com.feihe.coa.bean.SysUser;
import com.feihe.coa.mapper.SysLoginMapper;
import com.feihe.common.ResponseData;
import com.feihe.util.JWT; import net.sf.json.JSONObject; public class TokenInterceptor implements HandlerInterceptor{ @Autowired
private SysLoginMapper sysLoginMapper; public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception arg3)
throws Exception {
} public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView model) throws Exception {
} //拦截每个请求
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setCharacterEncoding("utf-8");
String token = request.getParameter("token");
ResponseData responseData;
//token不存在
if(null != token) {
SysUser user = JWT.unsign(token, SysUser.class);
//解密token后的user是否为null,一般都是token过期
if(null != user) {
if(user.getUserId() != 0) {
SysLogin login = new SysLogin();
login.setUserId(user.getUserId());
login.setState(1); // 登入状态为1
// 判断是否存在该用户的“登入”状态
List<SysLogin> loginList = sysLoginMapper.selectByUidAndState(login);
if(null !=loginList && loginList.size() > 0){
if(token.equals(loginList.get(0).getToken())){
return true;
}else {
responseData = ResponseData.forbidden();
responseMessage(response, response.getWriter(), responseData);
return false;
}
}else {
responseData = ResponseData.forbidden();
responseMessage(response, response.getWriter(), responseData);
return false;
}
}else {
responseData = ResponseData.forbidden();
responseMessage(response, response.getWriter(), responseData);
return false;
}
}else {
responseData = ResponseData.forbidden();
responseMessage(response, response.getWriter(), responseData);
return false;
}
}else {
responseData = ResponseData.forbidden();
responseMessage(response, response.getWriter(), responseData);
return false;
}
} //请求不通过,返回错误信息给客户端
private void responseMessage(HttpServletResponse response, PrintWriter out, ResponseData responseData) {
responseData = ResponseData.forbidden();
response.setContentType("application/json; charset=utf-8");
String json = JSONObject.fromObject(responseData).toString();
out.print(json);
out.flush();
out.close();
} }

基于Token的身份验证--JWT的更多相关文章

  1. 基于Token的身份验证——JWT

    初次了解JWT,很基础,高手勿喷. 基于Token的身份验证用来替代传统的cookie+session身份验证方法中的session. JWT是啥? JWT就是一个字符串,经过加密处理与校验处理的字符 ...

  2. 基于token的身份验证JWT

    传统身份验证的方法 HTTP 是一种没有状态的协议,也就是它并不知道是谁是访问应用.这里我们把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过下回这个客户端再发送请求时候,还得再验证一下 ...

  3. 基于Token的身份验证——JWT(转)

    本文转自:http://www.cnblogs.com/zjutzz/p/5790180.html 感谢作者 初次了解JWT,很基础,高手勿喷.基于Token的身份验证用来替代传统的cookie+se ...

  4. WebApi_基于Token的身份验证——JWT

    JWT是啥? JWT就是一个字符串,经过加密处理与校验处理的字符串,形式为: A.B.C A由JWT头部信息header加密得到B由JWT用到的身份验证信息json数据加密得到C由A和B加密得到,是校 ...

  5. 基于 Token 的身份验证:JSON Web Token(JWT)

    1.传统身份验证和JWT的身份验证 传统身份验证:       HTTP 是一种没有状态的协议,也就是它并不知道是谁是访问应用.这里我们把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过 ...

  6. (转)基于 Token 的身份验证

    原文:https://ninghao.net/blog/2834 最近了解下基于 Token 的身份验证,跟大伙分享下.很多大型网站也都在用,比如 Facebook,Twitter,Google+,G ...

  7. 基于 Token 的身份验证:JSON Web Token(附:Node.js 项目)

    最近了解下基于 Token 的身份验证,跟大伙分享下.很多大型网站也都在用,比如 Facebook,Twitter,Google+,Github 等等,比起传统的身份验证方法,Token 扩展性更强, ...

  8. [转载]基于 Token 的身份验证

    作者:王皓发布于:2015-08-07 22:06更新于:2015-08-07 22:07 最近了解下基于 Token 的身份验证,跟大伙分享下.很多大型网站也都在用,比如 Facebook,Twit ...

  9. JavaWeb—基于Token的身份验证

    传统身份验证的方法 HTTP Basic Auth HTTP Basic Auth简单点说明就是每次请求API时都提供用户的username和password,简言之,Basic Auth是配合RES ...

随机推荐

  1. Message Code 【27796】 Failed to connect to server 'hostname';port_ld': 'reason'.

    Message Code [27796] Failed to connect to server 'hostname';port_ld': 'reason'.Unable to connect to  ...

  2. python中的矩阵、多维数组----numpy

    https://docs.scipy.org/doc/numpy-dev/user/quickstart.html  (numpy官网一些教程) numpy教程:数组创建 python中的矩阵.多维数 ...

  3. Linux安装php运行环境

    安装apache: yum install httpd httpd-devel  启动apache: /etc/init.d/httpd start 此时输入服务器的IP地址,应该看到apache的服 ...

  4. 提取Unity游戏资源和脚本

    UnityStudio UnityStudio可以直接在自己的软件上查看图片.shader.文本.还能直接播放音频.甚至还能看场景Hierarchy视图的树状结构.强烈推荐用UnityStudio. ...

  5. MathType中公式不对齐怎么办

    MathType是一款专门用来编辑数学公式的数学公式编辑器,利用它可以在文档中快速编辑公式,与文字完美结合,可以编辑出各种各样的数学符号与公式,省下你不少的时间. 一.对齐上标与下标 MathType ...

  6. 国外大神说:在编程中使用If语句的潜在危险

    大多数编程语言中if语句主要有两个作用:验证输入以保护域免受错误数据的影响,以及处理域内业务逻辑.但是,Udi Dahan最近在阿姆斯特丹DDD欧洲会议上的发言中指出,我们一般很     当我们查看系 ...

  7. 07python之字符串的常用方法

    字符串作为python中常用的数据类型,掌握字符串的常用方法十分必要. 常用知识点: 1.字符串的3种格式化方法 2.字符串的strip()方法 3.字符串的join()方法 4.字符串可以切片 1. ...

  8. python文件和目录操作方法大全(含实例)【python】

    转自:http://www.jb51.net/article/48001.htm 一.python中对文件.文件夹操作时经常用到的os模块和shutil模块常用方法. 1.得到当前工作目录,即当前Py ...

  9. Qt监控Arduino开关状态(读取串口数据)

    setup.ini配置文件内容 [General] #游戏所在主机IP GameIp1=192.168.1.151 GameIp2=192.168.1.152 GameIp3=192.168.1.15 ...

  10. m2014-architecture-imgserver->Lighttpd +mod_mem_cache的效果简直太好了

    公司的图片服务器一直以来负载都比较高,原因是图片比较分散而且比较小.经常把iowait搞的特别的高.但是只有一台机器也法用squid,经测试squid和apache在同一台机器效果会很糟糕的.因为sq ...