赞
踩
JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑而独立的方法,用于在各方之间安全地将信息作为JSON对象传输。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公用/专用密钥对对JWT进行签名。
授权
信息交换
{
"alg": "HS256",
"typ": "JWT"
}
{
"username": "xizi",
"password": "1234"
}
1. Signature介绍
- HMACSHA256 (base64Ur1Encode(header) + "." + base64Ur1Encode(payload), secret)
2. 签名目的
3. 信息安全问题
<!--引入jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
@Test void contextLoads() { HashMap<String, Object> map = new HashMap<>(); Calendar instance = Calendar.getInstance(); instance.add(Calendar.SECOND,60); String token = JWT.create() .withHeader(map) //head .withClaim("userId", 21) //payload .withClaim("username", "xizi") .withExpiresAt(instance.getTime()) //过期时间 20秒之后 .sign(Algorithm.HMAC256("QWER"));//签名 System.out.println("-------------------------"); System.out.println(token); System.out.println("-------------------------"); }
生成结果
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJleHAiOjE2MDg3NzM2NDYsInVzZXJJZCI6MjEsInVzZXJuYW1lIjoieGl6aSJ9.
Cauw34h5LKkcb6RNpY_mMfbv190v7AeKGy-BX4Xj0z0
//验证
@Test
public void test(){
//创建验证对象
Verification verification = JWT.require(Algorithm.HMAC256("QWER"));//签名
JWTVerifier jwtVerifier = verification.build();
DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDg3NzQwMjEsInVzZXJJZCI6MjEsInVzZXJuYW1lIjoieGl6aSJ9.eo0iGbk4aZRyXcVH3_8GuxlTULn9VeOnGfqMeRFRV5U");
Claim userId = verify.getClaim("userId");
Claim username = verify.getClaim("username");
System.out.println(userId.asInt());
System.out.println(username.asString());
System.out.println("过期时间:"+verify.getExpiresAt());
}
1. 常见的异常
SignatureVerificationException: 签名不一致异常
TokenExpiredException: 令牌过期异常
AlgorithmMi smatchException: 算法不匹配异常
InvalidClaimException: 失效的payload异常
package com.xizi.utils; public class JWTUtils { //自定义签名 private static final String SIGN="XIZI"; /** * 生成token 三部分组成header.payload.sign * */ public static String getToken(Map<String,String> map){ Calendar instance = Calendar.getInstance(); instance.add(Calendar.DATE,7); //七天 过期时间 //创建jwt builder JWTCreator.Builder builder = JWT.create(); //payload 遍历map中的键值对 map.forEach((k,v)->{ builder.withClaim(k,v ); }); //指定过期时间 设置编码方式 String token = builder.withExpiresAt(instance.getTime()) .sign(Algorithm.HMAC256(SIGN)); return token; } /** * 验证token 并且放回DecodedJWT 获取token信息方法 */ public static DecodedJWT verify(String token){ //没有抛出异常 验证通过 return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token); } /** * 获取token信息方法 */ public static DecodedJWT getTokenInfo(String token){ DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token); return verify; } }
<dependencies> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--引入jwt--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> <!--引入mybais--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!--Druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.13</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.6</version> </dependency> </dependencies>
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/jwt?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.type-aliases-package=com.xizi.pojo
mybatis.mapper-locations=classpath:com/xizi/mapper/*.xml
logging.level.com.xizi.dao=debug
简单的一张表
package com.xizi.dao;
import com.xizi.pojo.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserDao {
User login(User user);
}
简单的一条查询语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xizi.dao.UserDao">
<select id="login" parameterType="User" resultType="User">
select * from user where name=#{name} and password=#{password}
</select>
</mapper>
UserService
package com.xizi.service;
import com.xizi.pojo.User;
public interface UserService {
User login(User user);
}
UserServiceImpl
package com.xizi.service; @Service @Transactional public class UserServiceImpl implements UserService { @Resource @Autowired private UserDao userDao; @Override @Transactional(propagation = Propagation.SUPPORTS) public User login(User user) { //根据接收用户名密码查询数据库 User user1 = userDao.login(user); if (user1!=null){ return user1; } throw new RuntimeException("登录失败~~"); } }
package com.xizi.controller; @RestController @Slf4j public class UserController { @Autowired private UserService userService; @GetMapping("/user/login") public Map<String,Object> login(User user){ log.info("用户名:[{}]", user.getName()); log.info("密码:[{}]", user.getPassword()); HashMap<String, Object> map = new HashMap<>(); try{ User userDB = userService.login(user); Map<String, String> payload = new HashMap<>(); payload.put("id",userDB.getId().toString()); payload.put("name",userDB.getName()); //生成JWT的令牌 String token = JWTUtils.getToken(payload); map.put("state",true); map.put("msg","登录成功"); map.put("token",token); //响应token }catch (Exception e){ map.put("state",false); map.put("msg","登录失败"); } return map; } @PostMapping("/user/test") public Map<String,Object> test(HttpServletRequest request){ HashMap<String, Object> map = new HashMap<>(); //处理自己的业务逻辑 String token=request.getHeader("token"); DecodedJWT verify = JWTUtils.verify(token); log.info("用户id:[{}]",verify.getClaim("id").asString()); log.info("用户name:[{}]",verify.getClaim("name").asString()); map.put("state",true); map.put("msg","请求成功"); map.put("id",verify.getClaim("id").asString()); map.put("msg",verify.getClaim("name").asString()); return map; } }
自定义拦截器
package com.xizi.interceptors; // 自定义拦截器 public class JWTInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //获取请求头header中的令牌 String token = request.getHeader("token"); HashMap<String, Object> map = new HashMap<>(); try { DecodedJWT verify = JWTUtils.verify(token); return true; //放行 }catch (SignatureVerificationException e){ e.printStackTrace(); map.put("msg","无效签名"); } catch (TokenExpiredException e) { e.printStackTrace(); map.put("msg","token过期"); }catch (AlgorithmMismatchException e){ e.printStackTrace(); map.put("msg","token算法不一致"); }catch (Exception e){ e.printStackTrace(); map.put("msg","token无效"); } map.put("state",false); //设置状态 //将map 转为json jackson String json=new ObjectMapper().writeValueAsString(map); response.setContentType("application/json;charset=UTF-8"); response.getWriter().println(json); return false; } }
添加自定义拦截器到项目中
package com.xizi.config; import com.xizi.interceptors.JWTInterceptor; 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 { //添加自定义拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JWTInterceptor()) // .addPathPatterns("/user/test") //其他接口token都验证 .addPathPatterns("/**") //所有接口token都验证 .excludePathPatterns("/user/**"); //所有的用户接口都放行 } }
在headers 添加token进行测试
http://localhost:8080/test/test
@RestController
public class Controller {
@GetMapping("/test/test")
public String test(String username, HttpServletRequest request){
request.getSession().setAttribute("username",username );
return "login-ok";
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。