简单说明:最近在搞权限这一块的东西,需要用到jwt进行token认证,才有了如下的demo演示 具体细节可以看gitbug,噗,不是bug是hub github地址:https://github.com/xuchao6969/jwt_demo 点击前边的gitbug也可以哦
项目是springboot+thymeleaf+mybatis+mysql搭建的
//pom核心依赖 <dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
//application.yml的核心配置
#token有效期 单位ms
expire_time: 9000000
#token加密secret
token_secret: jwtsecret
token的工具类,包括生成签名、校验、解析等方法

package com.xc.jwt_demo.util; import java.util.Date; import java.util.HashMap; import java.util.Map; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.interfaces.DecodedJWT; public class JwtTokenUtil { /** * 生成签名,过期时间可以从配置文件里读取 具体看方法调用处 * @param **username** * @param **password** * @return */ public static String sign(String username, String userId,Long expiretime,String secret) { try { // 设置过期时间 Date date = new Date(System.currentTimeMillis() + expiretime); // 私钥和加密算法 Algorithm algorithm = Algorithm.HMAC256(secret); // 设置头部信息 Map<String, Object> header = new HashMap<>(2); header.put("Type", "Jwt"); header.put("alg", "HS256"); // 返回token字符串 return JWT.create() .withHeader(header) .withClaim("loginName", username) .withClaim("userId", userId) .withExpiresAt(date) .sign(algorithm); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 检验token是否正确 * @param **token** * @return */ public static boolean verify(String token,String secret){ try { Algorithm algorithm = Algorithm.HMAC256(secret); JWTVerifier verifier = JWT.require(algorithm).build(); @SuppressWarnings("unused") DecodedJWT jwt = verifier.verify(token); return true; } catch (Exception e){ return false; } } /** * 从token中获取username信息 * @param **token** * @return */ public static String getUserName(String token){ try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("loginName").asString(); } catch (JWTDecodeException e){ e.printStackTrace(); return null; } } /** * token是否过期 * @param token */ public static Boolean checkExpire(String token) { try { DecodedJWT jwt = JWT.decode(token); //获取token过期时间 Date expiretime = jwt.getExpiresAt(); String etStr = String.valueOf(expiretime.getTime()); //获取系统当前时间 String nowTime = String.valueOf(System.currentTimeMillis()); //如果系统当前时间超过token过期时间返回false if(nowTime.compareTo(etStr)>0){ return false; } return true; } catch (JWTDecodeException e){ e.printStackTrace(); } return true; } }
token拦截器,针对所有的请求进行token认证 也是核心

package com.xc.jwt_demo.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import com.xc.jwt_demo.util.JwtTokenUtil;
@Component
public class TokenInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(TokenInterceptor.class);
//从配置文件application.yml进行加载
@Value("${token_secret}")
private String secret;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object)
throws Exception {
String str = request.getMethod();
if (str.equals("OPTIONS")) {
response.setStatus(HttpServletResponse.SC_OK);
return true;
}
response.setCharacterEncoding("utf-8");
String token = request.getHeader("token");
if (null != token) {
//校验token是否过期
boolean result = JwtTokenUtil.verify(token, secret);
if (result) {
logger.info("通过拦截器");
return true;
}
}
logger.info("认证失败");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("Token不存在或者已失效");
return false;
}
}
token拦截器的配置,主要用来配置不需要拦截路径,比如登录...

package com.xc.jwt_demo.config;
import java.util.ArrayList;
import java.util.List;
import com.xc.jwt_demo.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer{
private TokenInterceptor tokenInterceptor;
public InterceptorConfig(TokenInterceptor tokenInterceptor) {
this.tokenInterceptor = tokenInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor)
//拦截所有的请求
.addPathPatterns("/**")
//放开请求
.excludePathPatterns("/sys/test","/sys/login","/sys/doLogin","/js/**");
}
}
好了,下边的一坨坨代码就是常规的三层业务了,可以参考看一下UserServiceImpl里的生成token 解析token的调用,其他没啥大的价值 另外 我的token是存放在数据库的

package com.xc.jwt_demo.controller; import java.io.IOException; import java.util.Map; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.xc.jwt_demo.entity.User; import com.xc.jwt_demo.service.UserService; import com.xc.jwt_demo.util.JsonUtil; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping("/sys") public class LoginController { @Resource private UserService userService; //测试页面 @RequestMapping("/test") public String testThymeleaf(ModelMap model) { User user = new User(); user.setUsername("二叔"); user.setMobile("911"); model.addAttribute("user", user); return "/viewTest"; } //登录页面 @RequestMapping("/login") public String login(ModelMap model) { return "/login"; } @RequestMapping(value = "/doLogin") @ResponseBody public Integer login(User user) { Map<String, Object> resultMap = userService.login(user.getUsername(),user.getPassword()); Integer code = (Integer) resultMap.get("code"); return code; } //使用postman或者restclient进行token测试 @RequestMapping("/tokenTest") public void TokenTest(HttpServletRequest request,HttpServletResponse response) throws IOException { String token = request.getHeader("token"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("认证成功 Token是:"+token); } }

package com.xc.jwt_demo.service; import java.util.Map; public interface UserService { Map<String, Object> login(String username, String password); }

package com.xc.jwt_demo.service.impl; import java.util.HashMap; import java.util.Map; import com.xc.jwt_demo.dao.UserDao; import com.xc.jwt_demo.entity.User; import com.xc.jwt_demo.service.UserService; import com.xc.jwt_demo.util.JwtTokenUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.beans.factory.annotation.Value; @Service("userService") public class UserServiceImpl implements UserService { private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class); //从配置文件application.yml读取token有效期时间 单位ms @Value("${expire_time}") private Long expiretime; //读取加密的secret @Value("${token_secret}") private String secret; @Autowired private UserDao dao; @Override public Map<String, Object> login(String username,String password) { Map<String, Object> map = new HashMap<>(); User userObj = dao.getUserByUserName(username); //判断用户是否存在 if(null == userObj){ map.put("code", 0);//用户不存在 return map; } //判断密码是否正确 if(! password.equals(userObj.getPassword())){ map.put("code", -1);//密码错误 return map; } map.put("code", 1);//用户存在 String tokenObj = userObj.getToken(); //用户token不存在就创建token if(null == tokenObj || "".equals(tokenObj)){ String token = JwtTokenUtil.sign(userObj.getUsername(),userObj.getUserId(),expiretime,secret); logger.info("-----------创建token-------: "+ token); Map<String, Object> paraMap = new HashMap<>(); paraMap.put("token", token); paraMap.put("userId", userObj.getUserId()); dao.addToken2User(paraMap); map.put("token", token); }else{ //取出用户token String token = userObj.getToken(); logger.info("------存在的token-----: "+ token); //判断token是否过期 不过期返回,过期 新建token返回并插入数据库 if(JwtTokenUtil.checkExpire(token)){ logger.info("-------token存在且有效------: "+ token); map.put("token", token); }else{ String tokenStr = JwtTokenUtil.sign(userObj.getUsername(),userObj.getUserId(),expiretime,secret); logger.info("------token重新生成-----: "+ tokenStr); Map<String, Object> paraMap = new HashMap<>(); paraMap.put("token", tokenStr); paraMap.put("userId", userObj.getUserId()); dao.addToken2User(paraMap); map.put("token", tokenStr); } } map.put("userId", userObj.getUserId()); map.put("username", userObj.getUsername()); return map; } }

package com.xc.jwt_demo.dao; import org.apache.ibatis.annotations.Mapper; import com.xc.jwt_demo.entity.User; import java.util.Map; @Mapper public interface UserDao { User getUserByUserName(String username); void addToken2User(Map<String, Object> map); }

package com.xc.jwt_demo.entity;
import lombok.Data;
@Data
public class User {
private String userId;
private String username;
private String password;
private String email;
private String mobile;
private String status;
private String createBy;
private String createTime;
private String lastUpdateBy;
private String lastUpdateTime;
private String department;
private String realName;
private String establishVest;
private String delFlag;
private String token;
}

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.xc.jwt_demo.dao.UserDao"> <resultMap id="UserResultMap" type ="com.xc.jwt_demo.entity.User"> <id property="userId" column="user_id" /> <result property="username" column="username" /> <result property="password" column="password" /> <result property="email" column="email" /> <result property="mobile" column="mobile" /> <result property="status" column="status" /> <result property="createBy" column="create_by" /> <result property="createTime" column="create_time" /> <result property="lastUpdateBy" column="last_update_by" /> <result property="lastUpdateTime" column="last_update_time" /> <result property="department" column="department" /> <result property="realName" column="real_name" /> <result property="establishVest" column="establish_vest" /> <result property="delFlag" column="del_flag" /> <result property="token" column="token" /> </resultMap> <sql id="tableName">t_user</sql> <update id="addToken2User" parameterType="java.util.Map"> update <include refid="tableName" /> set token = #{token} where user_id = #{userId} </update> <select id="getUserByUserName" parameterType="String" resultMap="UserResultMap"> select * from <include refid="tableName" /> where username = #{username} </select> </mapper>
另外说一点,很重要,在html页面中必须加入<link rel=“shortcut icon” href="#" /> 否则会在测试的时候会出现 favicon.ico不存在的问题,从而导致测试失败 vue的话也会有类似的问题
下边是测试
首先不带token访问tokenTest页面
后台打印
然后登陆 获取token
后台打印
然后 把token粘到postman或者restClient中 设置好header token再次访问
后台打印
最终结果