博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jwt
阅读量:5135 次
发布时间:2019-06-13

本文共 13474 字,大约阅读时间需要 44 分钟。

1. JWT是什么

   JSON Web Token (JWT),它是目前最流行的跨域身份验证解决方案

 

 

2. 为什么使用JWT

   JWT的精髓在于:去中心化,数据是保存在客户端的。

 

 

3. JWT的工作原理

   1. 是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,示例如下:

      {"UserName": "Chongchong","Role": "Admin","Expire": "2018-08-08 20:15:56"}

 

   2. 之后,当用户与服务器通信时,客户在请求中发回JSON对象

    

   3. 为了防止用户篡改数据,服务器将在生成对象时添加签名,并对发回的数据进行验证

 

4. JWT组成

 

   一个JWT实际上就是一个字符串,它由三部分组成:头部(Header)、载荷(Payload)与签名(signature)

 

   JWT结构原理图:见资料“JWT的数据结构.jpg”

 

jwt所解决的问题及机制

后台:

过滤器 CorsFilter

 

package com.liuwenwu.vue.util;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * 配置tomcat允许跨域访问 *  * @author Administrator * */public class CorsFilter implements Filter {    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    // @Override    // public void doFilter(ServletRequest servletRequest, ServletResponse    // servletResponse, FilterChain filterChain)    // throws IOException, ServletException {    // HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;    //    // // Access-Control-Allow-Origin就是我们需要设置的域名    // // Access-Control-Allow-Headers跨域允许包含的头。    // // Access-Control-Allow-Methods是允许的请求方式    // httpResponse.addHeader("Access-Control-Allow-Origin", "*");// *,任何域名    // httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT,    // DELETE");    // // httpResponse.setHeader("Access-Control-Allow-Headers", "Origin,    // // X-Requested-With, Content-Type, Accept");    //    // // 允许请求头Token    // httpResponse.setHeader("Access-Control-Allow-Headers",    // "Origin,X-Requested-With, Content-Type, Accept, Token");    // HttpServletRequest req = (HttpServletRequest) servletRequest;    // System.out.println("Token=" + req.getHeader("Token"));    // if("OPTIONS".equals(req.getMethod())) {    // return;    // }    //    //    // filterChain.doFilter(servletRequest, servletResponse);    // }    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)            throws IOException, ServletException {        HttpServletResponse resp = (HttpServletResponse) servletResponse;        HttpServletRequest req = (HttpServletRequest) servletRequest;        // Access-Control-Allow-Origin就是我们需要设置的域名        // Access-Control-Allow-Headers跨域允许包含的头。        // Access-Control-Allow-Methods是允许的请求方式        resp.setHeader("Access-Control-Allow-Origin", "*");// *,任何域名        resp.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE");        // resp.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With,        // Content-Type, Accept");        // 允许客户端,发一个新的请求头jwt        resp.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With, Content-Type, Accept, jwt");        // 允许客户端,处理一个新的响应头jwt        resp.setHeader("Access-Control-Expose-Headers", "jwt");        // String sss = resp.getHeader("Access-Control-Expose-Headers");        // System.out.println("sss=" + sss);        // 允许请求头Token        // httpResponse.setHeader("Access-Control-Allow-Headers","Origin,X-Requested-With,        // Content-Type, Accept, Token");        // System.out.println("Token=" + req.getHeader("Token"));        if ("OPTIONS".equals(req.getMethod())) {
// axios的ajax会发两次请求,第一次提交方式为:option,直接返回即可 return; } filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { }}

 

 

过滤器 JwtFilter

package com.liuwenwu.vue.util;import java.io.IOException;import java.util.regex.Matcher;import java.util.regex.Pattern;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import io.jsonwebtoken.Claims;/** * * JWT验证过滤器,配置顺序 :CorsFilte-->JwtFilter-->struts2中央控制器 *  * @author Administrator * */public class JwtFilter implements Filter {    // 排除的URL,一般为登陆的URL(请改成自己登陆的URL)    private static String EXCLUDE = "^/vue/userAction_login\\.action?.*$";    private static Pattern PATTERN = Pattern.compile(EXCLUDE);    private boolean OFF = false;// true关闭jwt令牌验证功能    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    @Override    public void destroy() {    }    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)            throws IOException, ServletException {        HttpServletRequest req = (HttpServletRequest) request;        HttpServletResponse resp = (HttpServletResponse) response;        String path = req.getServletPath();        if (OFF || isExcludeUrl(path)) {
// 登陆直接放行 chain.doFilter(request, response); return; } // 从客户端请求头中获得令牌并验证 String jwt = req.getHeader(JwtUtils.JWT_HEADER_KEY); Claims claims = this.validateJwtToken(jwt); if (null == claims) { // resp.setCharacterEncoding("UTF-8"); resp.sendError(403, "JWT令牌已过期或已失效"); return; } else { String newJwt = JwtUtils.copyJwt(jwt, JwtUtils.JWT_WEB_TTL); resp.setHeader(JwtUtils.JWT_HEADER_KEY, newJwt); chain.doFilter(request, response); } } /** * 验证jwt令牌,验证通过返回声明(包括公有和私有),返回null则表示验证失败 */ private Claims validateJwtToken(String jwt) { Claims claims = null; try { if (null != jwt) { claims = JwtUtils.parseJwt(jwt); } } catch (Exception e) { e.printStackTrace(); } return claims; } /** * 是否为排除的URL * * @param path * @return */ private boolean isExcludeUrl(String path) { Matcher matcher = PATTERN.matcher(path); return matcher.matches(); } // public static void main(String[] args) { // String path = "/sys/userAction_doLogin.action?username=zs&password=123"; // Matcher matcher = PATTERN.matcher(path); // boolean b = matcher.matches(); // System.out.println(b); // }}

工具类 JwtUtils

package com.liuwenwu.vue.util;import java.util.Date;import java.util.Map;import java.util.UUID;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import org.apache.commons.codec.binary.Base64;import io.jsonwebtoken.Claims;import io.jsonwebtoken.JwtBuilder;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;/** * JWT验证过滤器:配置顺序 CorsFilte->JwtUtilsr-->StrutsPrepareAndExecuteFilter * */public class JwtUtils {    /**     * JWT_WEB_TTL:WEBAPP应用中token的有效时间,默认30分钟     */    public static final long JWT_WEB_TTL = 5 * 60 * 1000;    /**     * 将jwt令牌保存到header中的key     */    public static final String JWT_HEADER_KEY = "jwt";    // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。    private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;    private static final String JWT_SECRET = "f356cdce935c42328ad2001d7e9552a3";// JWT密匙    private static final SecretKey JWT_KEY;// 使用JWT密匙生成的加密key    static {        byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);        JWT_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");    }    private JwtUtils() {    }    /**     * 解密jwt,获得所有声明(包括标准和私有声明)     *      * @param jwt     * @return     * @throws Exception     */    public static Claims parseJwt(String jwt) {        Claims claims = Jwts.parser().setSigningKey(JWT_KEY).parseClaimsJws(jwt).getBody();        return claims;    }    /**     * 创建JWT令牌,签发时间为当前时间     *      * @param claims     *            创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)     * @param ttlMillis     *            JWT的有效时间(单位毫秒),当前时间+有效时间=过期时间     * @return jwt令牌     */    public static String createJwt(Map
claims, long ttlMillis) { // 生成JWT的时间,即签发时间 long nowMillis = System.currentTimeMillis(); // 下面就是在为payload添加各种标准声明和私有声明了 // 这里其实就是new一个JwtBuilder,设置jwt的body JwtBuilder builder = Jwts.builder() // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的 .setClaims(claims) // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。 // 可以在未登陆前作为身份标识使用 .setId(UUID.randomUUID().toString().replace("-", "")) // iss(Issuser)签发者,写死 // .setIssuer("zking") // iat: jwt的签发时间 .setIssuedAt(new Date(nowMillis)) // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放 // .setSubject("{}") // 设置签名使用的签名算法和签名使用的秘钥 .signWith(SIGNATURE_ALGORITHM, JWT_KEY) // 设置JWT的过期时间 .setExpiration(new Date(nowMillis + ttlMillis)); return builder.compact(); } /** * 复制jwt,并重新设置签发时间(为当前时间)和失效时间 * * @param jwt * 被复制的jwt令牌 * @param ttlMillis * jwt的有效时间(单位毫秒),当前时间+有效时间=过期时间 * @return */ public static String copyJwt(String jwt, Long ttlMillis) { Claims claims = parseJwt(jwt); // 生成JWT的时间,即签发时间 long nowMillis = System.currentTimeMillis(); // 下面就是在为payload添加各种标准声明和私有声明了 // 这里其实就是new一个JwtBuilder,设置jwt的body JwtBuilder builder = Jwts.builder() // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的 .setClaims(claims) // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。 // 可以在未登陆前作为身份标识使用 //.setId(UUID.randomUUID().toString().replace("-", "")) // iss(Issuser)签发者,写死 // .setIssuer("zking") // iat: jwt的签发时间 .setIssuedAt(new Date(nowMillis)) // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放 // .setSubject("{}") // 设置签名使用的签名算法和签名使用的秘钥 .signWith(SIGNATURE_ALGORITHM, JWT_KEY) // 设置JWT的过期时间 .setExpiration(new Date(nowMillis + ttlMillis)); return builder.compact(); }}

生成jwt令牌 UserAction

package com.liuwenwu.vue.web;import java.util.HashMap;import java.util.Map;import com.fasterxml.jackson.databind.ObjectMapper;import com.liuwenwu.base.web.BaseAction;import com.liuwenwu.vue.biz.UserBiz;import com.liuwenwu.vue.entity.User;import com.liuwenwu.vue.util.JsonData;import com.liuwenwu.vue.util.JwtUtils;import com.liuwenwu.vue.util.PageBean;import com.liuwenwu.vue.util.ResponseUtil;import com.liuwenwu.vue.util.StringUtils;import com.opensymphony.xwork2.ModelDriven;public class UserAction extends BaseAction implements ModelDriven
{ private UserBiz userBiz; private User user = new User(); public UserBiz getUserBiz() { return userBiz; } public void setUserBiz(UserBiz userBiz) { this.userBiz = userBiz; } public String login() { ObjectMapper om = new ObjectMapper(); JsonData jsonData = null; try { if(StringUtils.isBlank(user.getUname()) || StringUtils.isBlank(user.getPwd())) { jsonData = new JsonData(0, "用户或者密码为空", user); }else { User u = this.userBiz.login(user);// 生成jwt令牌 Map
claims = new HashMap
(); claims.put("uname",user.getUname()); claims.put("pwd", user.getPwd()); String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL); response.setHeader(JwtUtils.JWT_HEADER_KEY, jwt); jsonData = new JsonData(1, "登录成功", u); } } catch (Exception e) { e.printStackTrace(); jsonData = new JsonData(0, "用户或者密码错误", user); }finally { try { ResponseUtil.write(response, om.writeValueAsString(jsonData)); } catch (Exception e) { e.printStackTrace(); } } return null; } public String getAsyncData() { ObjectMapper om = new ObjectMapper(); try { Thread.sleep(6000); ResponseUtil.write(response, om.writeValueAsString("http://www.javaxl.com")); } catch (Exception e) { e.printStackTrace(); } return null; } @Override public User getModel() { return user; }}

前端:

state.js

export default {    resturantName: '飞歌餐馆',    jwt:null}

mutations.js

export default {  setJwt: (state, payload) => {    state.jwt = payload.jwt;  }}

getters.js

export default {  getJwt: (state) => {    return state.jwt;  }}

http.js 配置拦截器 

// 请求拦截器axios.interceptors.request.use(function(config) {  // 将jwt令牌塞到request Headers里面    var jwt = window.vm.$store.getters.getJwt;    config.headers['jwt'] = jwt;    return config;}, function(error) {    return Promise.reject(error);});// 响应拦截器axios.interceptors.response.use(function(response) {  // 将jwt令牌塞到response Headers里面    // debugger;    var jwt = response.headers['jwt'];    if(jwt){        window.vm.$store.commit('setJwt',{jwt:jwt});    }    return response;}, function(error) {    return Promise.reject(error);});

效果:

 

转载于:https://www.cnblogs.com/liuwenwu9527/p/11372443.html

你可能感兴趣的文章
【03月04日】A股滚动市盈率PE历史新低排名
查看>>
Xcode5和ObjC新特性
查看>>
jvm slot复用
查看>>
高并发系统数据库设计
查看>>
LibSVM for Python 使用
查看>>
入坑的开始~O(∩_∩)O~
查看>>
Centos 7.0 安装Mono 3.4 和 Jexus 5.6
查看>>
Windows 7 上安装Visual Studio 2015 失败解决方案
查看>>
iOS按钮长按
查看>>
Shell流程控制
查看>>
CSS属性值currentColor
查看>>
[Leetcode|SQL] Combine Two Tables
查看>>
《DSP using MATLAB》Problem 7.37
查看>>
ROS lesson 1
查看>>
js笔记
查看>>
c风格字符串函数
查看>>
python基础学习第二天
查看>>
java可重入锁reentrantlock
查看>>
浅谈卷积神经网络及matlab实现
查看>>
struts2学习(9)struts标签2(界面标签、其他标签)
查看>>