目录

1. JWT是什么

2. 为什么使用JWT

3. JWT的工作原理

4. JWT组成

4.1 Header

4.2 Payload(负荷/载荷)

①. Reserved claims(保留)

②. Public claims,略(公共)

③. Private claims(私有)

4.3 signature

5. JWT的验证过程

6. JWT令牌刷新思路


1. JWT是什么

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

2. 为什么使用JWT

JWT的精髓在于:“去中心化”,数据是保存在客户端的。(减轻服务器端的压力)

3. JWT的工作原理

3.1、 是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,示例如下:
      {"UserName": "Chongchong","Role": "Admin","Expire": "2018-08-08 20:15:56"}

 3.2、 之后,当用户与服务器通信时,客户在请求中发回JSON对象JWT
    
   3.3、为了防止用户篡改数据,服务器将在生成对象时添加签名,并对发回的数据进行验证

4. JWT组成

一个JWT实际上就是一个字符串。

它由三部分组成:头部(Header)、载荷(Payload)与签名(signature)

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

JWT实际结构:

eyJhbGciOiJIUzI1NiJ9.
        eyJzdWIiOiJ7fSIsImlzcyI6InpraW5nIiwiZXhwIjoxNTYyODUwMjM3LCJpYXQiOjE1NjI4NDg0MzcsImp0aSI6ImM5OWEyMzRmMDc4NzQyZWE4YjlmYThlYmYzY2VhNjBlIiwidXNlcm5hbWUiOiJ6c3MifQ.
        WUfqhFTeGzUZCpCfz5eeEpBXBZ8-lYg1htp-t7wD3I4

它是一个很长的字符串,中间用点(.)分隔成三个部分。

注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。写成一行,就是下面的样子:Header.Payload.Signature

4.1 Header

{"typ":"JWT","alg":"HS256"}
      这个json中的typ属性,用来标识整个token字符串是一个JWT字符串;它的alg属性,用来说明这个JWT签发的时候所使用的签名和摘要算法

typ跟alg属性的全称其实是type跟algorithm,分别是类型跟算法的意思。之所以都用三个字母来表示,也是基于JWT最终字符串大小的考虑,

同时也是跟JWT这个名称保持一致,这样就都是三个字符了…typ跟alg是JWT中标准中规定的属性名称

4.2 Payload(负荷/载荷)

{"sub":"123","name":"Tom","admin":true}(键值对)

payload用来承载要传递的数据(容器),它的json结构实际上是对JWT要传递的数据的一组声明,这些声明被JWT标准称为claims,
      它的一个“属性值对”其实就是一个claim(要求),
      每一个claim的都代表特定的含义和作用。
      
      注1:英文“claim”就是要求的意思

注2:如上面结构中的sub代表这个token(令牌)的所有人,存储的是所有人的ID;name表示这个所有人的名字;admin表示所有人是否管理员的角色。
           当后面对JWT进行验证的时候,这些claim都能发挥特定的作用

注3:根据JWT的标准,这些claims可以分为以下三种类型:

①. Reserved claims(保留)

它的含义就像是编程语言的保留字一样,属于JWT标准里面规定的一些claim。JWT标准里面定义好的claim有:
              iss(Issuser):代表这个JWT的签发主体; 
              sub(Subject):代表这个JWT的主体,即它的所有人; 
              aud(Audience):代表这个JWT的接收对象; 
              exp(Expiration time):是一个时间戳,代表这个JWT的过期时间; 
              nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的; 
              iat(Issued at):是一个时间戳,代表这个JWT的签发时间; 
              jti(JWT ID):是JWT的唯一标识。

②. Public claims,略(公共)

③. 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

4.3 signature

签名是把header(头部)和payload(载荷)对应的json结构进行base64url编码之后得到的两个串用英文句点号拼接起来,然后根据header里面alg指定的签名算法生成出来的。
       算法不同,签名结果不同。以alg: HS256为例来说明前面的签名如何来得到。
       
       按照前面alg可用值的说明,HS256其实包含的是两种算法:HMAC算法和SHA256算法,前者用于生成摘要,后者用于对摘要进行数字签名。这两个算法也可以用HMACSHA256来统称

5. JWT的验证过程

它验证的方法其实很简单,只要把header做base64url解码,就能知道JWT用的什么算法做的签名,然后用这个算法,再次用同样的逻辑对header和payload做一次签名,
   并比较这个签名是否与JWT本身包含的第三个部分的串是否完全相同,只要不同,就可以认为这个JWT是一个被篡改过的串,自然就属于验证失败了。
   接收方生成签名的时候必须使用跟JWT发送方相同的密钥
      
   注1:在验证一个JWT的时候,签名认证是每个实现库都会自动做的,但是payload的认证是由使用者来决定的。因为JWT里面可能会包含一个自定义claim,
        所以它不会自动去验证这些claim,以jjwt-0.7.0.jar为例:
        A 如果签名认证失败会抛出如下的异常:
          io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
       即签名错误,JWT的签名与本地计算机的签名不匹配 
        B JWT过期异常
          io.jsonwebtoken.ExpiredJwtException: JWT expired at 2017-06-13T11:55:56Z. Current time: 2017-06-13T11:55:57Z, a difference of 1608 milliseconds.  Allowed

注2:认证失败,返回401 Unauthorized响应

注3:认证服务作为一个Middleware HOOK 对请求进行拦截,首先在cookie中查找Token信息,如果没有找到,则在HTTP Authorization Head中查找

代码案例:

---------------------------------JwtUtilsimport 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 = 30 * 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密匙生成的加密keystatic {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<String, Object> claims, long ttlMillis) {// 生成JWT的时间,即签发时间long nowMillis = System.currentTimeMillis();// 下面就是在为payload添加各种标准声明和私有声明了// 这里其实就是new一个JwtBuilder,设置jwt的bodyJwtBuilder 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的bodyJwtBuilder 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();}
}---------------------------------JwtDemoimport java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;import org.junit.Test;import io.jsonwebtoken.Claims;public class JwtDemo {private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");@Testpublic void test1() {// 生成JWT//私有申明Map<String, Object> claims = new HashMap<String, Object>();claims.put("username", "zss");claims.put("age", 18);//生成JWT令牌=头部.载荷.签名String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL);System.out.println(jwt);Claims parseJwt = JwtUtils.parseJwt(jwt);for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {System.out.println(entry.getKey() + "=" + entry.getValue());}Date d1 = parseJwt.getIssuedAt();Date d2 = parseJwt.getExpiration();System.out.println("令牌签发时间:" + sdf.format(d1));System.out.println("令牌过期时间:" + sdf.format(d2));}@Testpublic void test2() {// 解析oldJwt//JWT令牌过期异常:io.jsonwebtoken.ExpiredJwtException//JWT令牌签名异常:io.jsonwebtoken.SignatureExceptionString newJwt="eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTI3MDE5MjEsImlhdCI6MTY1MjcwMDEyMSwiYWdlIjoxOCwianRpIjoiODEyNGYzZGFhZjBiNDJiZTlhNGZmYjZiNTJmZmUxMWEiLCJ1c2VybmFtZSI6InpzcyJ9.SQDj6VNQlXzWIjeEVJp8WObjHfsfP05bwyWiKWueX70";
//      String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI5MDMzNjAsImlhdCI6MTU2MjkwMTU2MCwiYWdlIjoxOCwianRpIjoiZDVjMzE4Njg0MDcyNDgyZDg1MDE5ODVmMDY3OGQ4NjkiLCJ1c2VybmFtZSI6InpzcyJ9.XDDDRRq5jYq5EdEBHtPm7GcuBz4S0VhDTS1amRCdf48";Claims parseJwt = JwtUtils.parseJwt(newJwt);for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {System.out.println(entry.getKey() + "=" + entry.getValue());}Date d1 = parseJwt.getIssuedAt();Date d2 = parseJwt.getExpiration();System.out.println("令牌签发时间:" + sdf.format(d1));System.out.println("令牌过期时间:" + sdf.format(d2));}@Testpublic void test3() {// 复制jwt,并延时30秒String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTI3MDE5MjEsImlhdCI6MTY1MjcwMDEyMSwiYWdlIjoxOCwianRpIjoiODEyNGYzZGFhZjBiNDJiZTlhNGZmYjZiNTJmZmUxMWEiLCJ1c2VybmFtZSI6InpzcyJ9.SQDj6VNQlXzWIjeEVJp8WObjHfsfP05bwyWiKWueX70";String jwt = JwtUtils.copyJwt(oldJwt, JwtUtils.JWT_WEB_TTL);Claims parseJwt = JwtUtils.parseJwt(jwt);for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {System.out.println(entry.getKey() + "=" + entry.getValue());}Date d1 = parseJwt.getIssuedAt();Date d2 = parseJwt.getExpiration();System.out.println("令牌签发时间:" + sdf.format(d1));System.out.println("令牌过期时间:" + sdf.format(d2));}@Testpublic void test4() {// 测试JWT的有效时间//载荷里的私有申明Map<String, Object> claims = new HashMap<String, Object>();claims.put("username", "zss");String jwt = JwtUtils.createJwt(claims, 3 * 1000L);System.out.println(jwt);Claims parseJwt = JwtUtils.parseJwt(jwt);Date d1 = parseJwt.getIssuedAt();Date d2 = parseJwt.getExpiration();System.out.println("令牌签发时间:" + sdf.format(d1));System.out.println("令牌过期时间:" + sdf.format(d2));}@Testpublic void test5() {// 三秒后再解析上面过期时间只有三秒的令牌,因为过期则会报错io.jsonwebtoken.ExpiredJwtExceptionString oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI4NTMzMzAsImlhdCI6MTU2Mjg1MzMyNywidXNlcm5hbWUiOiJ6c3MifQ.e098Vj9KBlZfC12QSDhI5lUGRLbNwb27lrYYSL6JwrQ";Claims parseJwt = JwtUtils.parseJwt(oldJwt);// 过期后解析就报错了,下面代码根本不会执行Date d1 = parseJwt.getIssuedAt();Date d2 = parseJwt.getExpiration();System.out.println("令牌签发时间:" + sdf.format(d1));System.out.println("令牌过期时间:" + sdf.format(d2));}
}

运行结果:

6. JWT令牌刷新思路

  6.1 登陆成功后,将生成的JWT令牌通过响应头返回给客户端
 
  6.2 WEB APP项目每次请求后台数据时(将JWT令牌从请求头中带过来),
      验证通过,刷新JWT,并保存在响应头返回给客户端,有效时间30分钟
      JwtFilter

package jwt;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 = "^/userAction\\.action?.*$";private static Pattern PATTERN = Pattern.compile(EXCLUDE);private boolean OFF = false;// true关闭jwt令牌验证功能@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void destroy() {}@Overridepublic 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);// }}

注1:修改CorsFilter添加允许的新的请求头“jwt”

代码如下:

package 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 {@Overridepublic 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);// }@Overridepublic 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");// 允许客户端,发一个新的请求头jwtresp.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With, Content-Type, Accept, jwt");// 允许客户端,处理一个新的响应头jwtresp.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);}@Overridepublic void destroy() {}
}

注2:原来在默认的请求上, 浏览器只能访问以下默认的 响应头
       Cache-Control
       Content-Language
       Content-Type
       Expires
       Last-Modified
       Pragma
       如果想让浏览器能访问到其他的 响应头的话 需要在服务器上设置 Access-Control-Expose-Headers
       Access-Control-Expose-Headers : 'jwt'

// CorsFilter 允许客户端,发一个新的请求头jwt
       resp.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With, Content-Type, Accept, jwt");

注3:axios从响应头获得jwt令牌并保存到vuex      delayed:延时
       这里有个问题如何获得项目中Vue的根实例,解决方案:修改main.js
               window.vm = new Vue({...});

其它vuex的操作就照旧

注4:退出系统请清空vuex中的内容哦

注5:刷新页面会导致vuex中的state清空,解决方案在前面一章   (* ̄︶ ̄)

代码案例:

------------------后台UserAction
package action;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import com.fasterxml.jackson.databind.ObjectMapper;
import com.zking.mvc.framework.DispatcherAction;
import com.zking.mvc.framework.ModelDriver;
import com.zking.vue.dao.UserDao;
import com.zking.vue.entity.User;
import com.zking.vue.jwt.JwtUtils;public class UserAction extends DispatcherAction implements ModelDriver<User> {private User user=new User();private UserDao userDao=new UserDao();private ObjectMapper mapper=new ObjectMapper();@Overridepublic User getModel() {return user;}public String userLogin(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{Map<String,Object> json=new HashMap<String,Object>();User login = userDao.Login(user);if(user.getPassword().equals(login.getPassword())&&user.getUsername().equals(login.getUsername())) {json.put("code","1");json.put("msg", "用户登录成功");//生成JWT,并设置到response响应头中String jwt=JwtUtils.createJwt(json, JwtUtils.JWT_WEB_TTL);resp.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);}else {json.put("code", "-1");json.put("msg", "用户名或密码错误");}mapper.writeValue(resp.getOutputStream(),json);return null;}
}------------------前端src/main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'import ElementUI from 'element-ui' //新添加1
import 'element-ui/lib/theme-chalk/index.css' //新添加2,避免后期打包样式不同,要放在import App from './App';之前import App from './App'
import router from './router'Vue.use(ElementUI) //新添加3
Vue.config.productionTip = falseimport axios from '@/api/http' //vue项目对axios的全局配置
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)import store from '@/store'//开发环境下才会引入mockjs
// process.env.MOCK && require('@/mock')/* eslint-disable no-new */
window.vm=new Vue({el: '#app',router,store, //在main.js中导入store实例components: {App},template: '<App/>'
})------------------前端api/http.js/*** vue项目对axios的全局配置*/
import axios from 'axios'
import qs from 'qs'//引入action模块,并添加至axios的类属性urls上
import action from '@/api/action'
axios.urls = action// axios默认配置
axios.defaults.timeout = 10000; // 超时时间
// axios.defaults.baseURL = 'http://localhost:8080/j2eeVue'; // 默认地址
axios.defaults.baseURL = action.SERVER;//整理数据
// 只适用于 POST,PUT,PATCH,transformRequest` 允许在向服务器发送前,修改请求数据
axios.defaults.transformRequest = function(data) {data = qs.stringify(data);return data;
};// 请求拦截器
axios.interceptors.request.use(function(config) {// 每一次向服务器端发送请求时,先由请求拦截器在此处获取vuex中的令牌并存入request请求头带入后端(登录请求除外)console.log(config);// 先从vuex中获取jwt令牌let jwt=window.vm.$store.getters.getJwt;if(null!=jwt)config.headers['jwt']=jwt;return config;
}, function(error) {return Promise.reject(error);
});// 响应拦截器
axios.interceptors.response.use(function(response) {// 从服务器端回传过来的response响应头中获取jwt令牌并存入vuex中console.log(response);let jwt=response.headers['jwt'];console.log(jwt);window.vm.$store.commit('setJwt',{jwt:jwt});return response;
}, function(error) {return Promise.reject(error);
});// // 路由请求拦截
// // http request 拦截器
// axios.interceptors.request.use(
//  config => {
//      //config.data = JSON.stringify(config.data);
//      //config.headers['Content-Type'] = 'application/json;charset=UTF-8';
//      //config.headers['Token'] = 'abcxyz';
//      //判断是否存在ticket,如果存在的话,则每个http header都加上ticket
//      // if (cookie.get("token")) {
//      //  //用户每次操作,都将cookie设置成2小时
//      //  cookie.set("token", cookie.get("token"), 1 / 12)
//      //  cookie.set("name", cookie.get("name"), 1 / 12)
//      //  config.headers.token = cookie.get("token");
//      //  config.headers.name = cookie.get("name");
//      // }
//      return config;
//  },
//  error => {
//      return Promise.reject(error.response);
//  });// // 路由响应拦截
// // http response 拦截器
// axios.interceptors.response.use(
//  response => {
//      if (response.data.resultCode == "404") {
//          console.log("response.data.resultCode是404")
//          // 返回 错误代码-1 清除ticket信息并跳转到登录页面
//          //      cookie.del("ticket")
//          //      window.location.href='http://login.com'
//          return
//      } else {
//          return response;
//      }
//  },
//  error => {
//      return Promise.reject(error.response) // 返回接口返回的错误信息
//  });export default axios;------------------前端vuex里的代码和上一章一样,就不给了~~~

运行结果:

Vue --- jwt相关推荐

  1. php vue jwt 实战,Vue路由之JWT身份认证的实现方法

    一.JWT身份认证简介 JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案,相较于session机制,服务器就不需要保存任何 session 数据了,也就是说,服务器变成无状态了 ...

  2. springboot+vue jwt校验token 单点登录

    SSO(Single Sign On)模式 CAS单点登录.OAuth2 分布式,SSO(single sign on)模式:单点登录英文全称Single Sign On,简称就是SSO.它的解释是: ...

  3. VUE+SpringBoot+JWT实现token验证,SSO单点登录

    Session的产生: 在说session是啥之前,我们先来说说为什么会出现session会话,它出现的机理是什么?我们知道,我们用浏览器打开一个网页,用到的是HTTP协议,htpp协议是无状态的,什 ...

  4. Vue之jwt(跨域身份验证,令牌)

    1. JWT是什么 JSON Web Token (JWT),它是目前最流行的跨域身份验证解决方案 2. 为什么使用JWT JWT的精髓在于:"去中心化",数据是保存在客户端的. ...

  5. springboot+vue项目使用jwt token验证登录,token验证失败

    boolean result= JwtUtil.verify(token.substring(1,token.length()-1)); 验证token,可能携带了开头和结尾的双引号,只要像上面的截取 ...

  6. ubantu20.04服务器使用docker以及docker-compose编排部署前后端分离个人博客项目(springboot+vue+mysql+redis)

    ubantu20.04使用docker部署前后端分离个人博客项目(springboot+vue+mysql+redis) 前言 本博客是基于springboot+vue+jwt+redis+mysql ...

  7. VUE+Spring Boot整合MyBatis实现前后端分离项目壁纸网站

    目录 前言 一.项目运行 二.环境需要 三.技术栈 四.项目说明 五.后端代码 前言 每次换桌面,壁纸总是不好找,搜索图片得不到好的索引与反馈,很难找到自己喜欢的壁纸,而壁纸网站可以免去我们去寻找壁纸 ...

  8. CoderHub接口文档

    CoderHub接口文档 Coderhub旨在创建一个程序员分享生活动态的平台. 完成的功能如下: 用户管理系统 内容管理系统 内容评论管理 内容标签管理 文件管理系统 接口完整请求示例:http:/ ...

  9. SpringBoot集成 Windows2012 AD 认证服务

    目录 1.应用背景 2.Windows2012 AD 服务安装及验证是否成功 2.1.Microsoft Windows Server 2012 Ad域搭建 2.2.安装完毕后如何验证是否成功 3.S ...

最新文章

  1. Swift学习——Swift解释特定的基础(七)
  2. mysql中自己定义函数编程
  3. SQL2000常见安装中问题解决方案
  4. 简单看正则(grep)
  5. 【涛声依旧】华为的“大服务”
  6. ChineseGLUE(CLUE):针对中文自然语言理解任务的基准平台
  7. 基于Anaconda安装GPU版PyTorch深度学习开发环境
  8. Linux bashrc和profile的用途和区别
  9. python递归查找_[Python]递归查找文件(最简洁)
  10. PDF Bookmark Extract1.0 release(Free Version) PDF书签抽取器(iTextSharp的利用)
  11. Python大数据处理扩展库pySpark用法精要
  12. 【报告分享】2020-2021视频号发展现状与趋势.pdf(附下载链接)
  13. r语言算巢式设计方差分析_R语言进行方差分析
  14. 从七种封装类型,看芯片封装发展史
  15. 8lag加速器无法建立计算机,8LAG 720错误解决办法
  16. 解决引用微信公众号获取的图片不能正常显示的问题,显示改图片来自微信公众号
  17. java使用ffmpeg实现视频切割
  18. centos系统 -官网下载mysql
  19. 仿照vue实现双向数据绑定兼容IE6
  20. 浅谈 Nyquist–Shannon(奈奎斯特-香农)采样定理

热门文章

  1. 【vscode------tab键切换补全enter选择补全】
  2. 索尼投屏无法显示服务器,索尼投屏显示无法播放视频
  3. Java——通过Java代码连接ftp服务器
  4. 在uniapp/微信小程序设置scrollTop初始值
  5. pycharm xp_pycharm winxp版软件下载-pycharm winxp最新版v2020.2.1 电脑版 - 极光下载站
  6. wordpress插件_WordPress插件样板第3部分:最后一步
  7. dynaform5.8.1曲面网格划分
  8. 网五服务器在哪个文件夹,5-4-在服务器中获取和放置文件-【管理文件】-奇秀广告(奇秀网)Dreamweaver CC2018入门及Dreamweaver教程...
  9. 程序猿也爱学英语(上),有图有真相
  10. 取得Google地图开发密钥Api key