Jwt在Java项目中的简单实际应用 女爷i 2022-11-26 04:00 205阅读 0赞 ## 1.什么是jwt ## 双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准([RFC 7519][]),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。**简洁(Compact)**: 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快** 自包含(Self-contained)**:负载中包含了所有用户所需要的信息,避免了多次查询数据库。 ## 2.Jwt在javaweb项目中的简单使用 ## 第一步:引入maven依赖 <!--引入JWT依赖,由于是基于Java,所以需要的是java-jwt--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> 第二步:创建两个注解,拦截器通过注释区分是否进行权限拦截(详情参考上一篇文章[JWT在JAVAWEB项目中的应用核心步骤解读][JWT_JAVAWEB] [https://www.cnblogs.com/jimisun/p/9480886.html][JWT_JAVAWEB]) package com.pjb.springbootjjwt.jimisun; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface LoginToken { boolean required() default true; } package com.pjb.springbootjjwt.jimisun; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface CheckToken { boolean required() default true; } 第三步:编写JwtUtil工具类(生成token,解析token,校验token) package com.pjb.springbootjjwt.jimisun; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * @Author:jimisun * @Description: * @Date:Created in 14:08 2018/8/15 * @Modified By: */ public class JwtUtil { /** * 用户登录成功后生成Jwt * 使用Hs256算法 私匙使用用户密码 * * @param ttlMillis jwt过期时间 * @param user 登录成功的user对象 * @return */ public static String createJWT(long ttlMillis, User user) { //指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; //生成JWT的时间 long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); //创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的) Map<String, Object> claims = new HashMap<String, Object>(); claims.put("id", user.getId()); claims.put("username", user.getUsername()); claims.put("password", user.getPassword()); //生成签名的时候使用的秘钥secret,这个方法本地封装了的,一般可以从本地配置文件中读取,切记这个秘钥不能外露哦。它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。 String key = user.getPassword(); //生成签发人 String subject = user.getUsername(); //下面就是在为payload添加各种标准声明和私有声明了 //这里其实就是new一个JwtBuilder,设置jwt的body JwtBuilder builder = Jwts.builder() //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的 .setClaims(claims) //设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。 .setId(UUID.randomUUID().toString()) //iat: jwt的签发时间 .setIssuedAt(now) //代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。 .setSubject(subject) //设置签名使用的签名算法和签名使用的秘钥 .signWith(signatureAlgorithm, key); if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); //设置过期时间 builder.setExpiration(exp); } return builder.compact(); } /** * Token的解密 * @param token 加密后的token * @param user 用户的对象 * @return */ public static Claims parseJWT(String token, User user) { //签名秘钥,和生成的签名的秘钥一模一样 String key = user.getPassword(); //得到DefaultJwtParser Claims claims = Jwts.parser() //设置签名的秘钥 .setSigningKey(key) //设置需要解析的jwt .parseClaimsJws(token).getBody(); return claims; } /** * 校验token * 在这里可以使用官方的校验,我这里校验的是token中携带的密码于数据库一致的话就校验通过 * @param token * @param user * @return */ public static Boolean isVerify(String token, User user) { //签名秘钥,和生成的签名的秘钥一模一样 String key = user.getPassword(); //得到DefaultJwtParser Claims claims = Jwts.parser() //设置签名的秘钥 .setSigningKey(key) //设置需要解析的jwt .parseClaimsJws(token).getBody(); if (claims.get("password").equals(user.getPassword())) { return true; } return false; } } 第四步:编写拦截器拦截请求进行权限验证 package com.pjb.springbootjjwt.interceptor; import com.auth0.jwt.JWT; import com.auth0.jwt.exceptions.JWTDecodeException; import com.pjb.springbootjjwt.jimisun.CheckToken; import com.pjb.springbootjjwt.jimisun.JwtUtil; import com.pjb.springbootjjwt.jimisun.LoginToken; import com.pjb.springbootjjwt.jimisun.User; import com.pjb.springbootjjwt.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; /** * jimisun */ public class AuthenticationInterceptor implements HandlerInterceptor { @Autowired UserService userService; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { // 从 http 请求头中取出 token String token = httpServletRequest.getHeader("token"); // 如果不是映射到方法直接通过 if (!(object instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) object; Method method = handlerMethod.getMethod(); //检查是否有LoginToken注释,有则跳过认证 if (method.isAnnotationPresent(LoginToken.class)) { LoginToken loginToken = method.getAnnotation(LoginToken.class); if (loginToken.required()) { return true; } } //检查有没有需要用户权限的注解 if (method.isAnnotationPresent(CheckToken.class)) { CheckToken checkToken = method.getAnnotation(CheckToken.class); if (checkToken.required()) { // 执行认证 if (token == null) { throw new RuntimeException("无token,请重新登录"); } // 获取 token 中的 user id String userId; try { userId = JWT.decode(token).getClaim("id").asString(); } catch (JWTDecodeException j) { throw new RuntimeException("访问异常!"); } User user = userService.findUserById(userId); if (user == null) { throw new RuntimeException("用户不存在,请重新登录"); } Boolean verify = JwtUtil.isVerify(token, user); if (!verify) { throw new RuntimeException("非法访问!"); } return true; } } return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } } 配置拦截器(ps:我使用的是springboot,大家使用ssm配置拦截器的方式不一样) package com.pjb.springbootjjwt.interceptorconfig; import com.pjb.springbootjjwt.interceptor.AuthenticationInterceptor; 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 { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) .addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录 } @Bean public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); } } 第五步:在示例Controller中的实际应用 package com.pjb.springbootjjwt.jimisun; import com.alibaba.fastjson.JSONObject; import com.pjb.springbootjjwt.annotation.PassToken; import com.pjb.springbootjjwt.annotation.UserLoginToken; import com.pjb.springbootjjwt.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.UUID; /** * @Author:jimisun * @Description: * @Date:Created in 15:04 2018/8/15 * @Modified By: */ @RestController @RequestMapping("/api") public class UserController { @Autowired private UserService userService; //登录 @PostMapping("/login") @LoginToken public Object login(@RequestBody @Valid com.pjb.springbootjjwt.jimisun.User user) { JSONObject jsonObject = new JSONObject(); com.pjb.springbootjjwt.jimisun.User userForBase = userService.findByUsername(user); if (userForBase == null) { jsonObject.put("message", "登录失败,用户不存在"); return jsonObject; } else { if (!userForBase.getPassword().equals(user.getPassword())) { jsonObject.put("message", "登录失败,密码错误"); return jsonObject; } else { String token = JwtUtil.createJWT(6000000, userForBase); jsonObject.put("token", token); jsonObject.put("user", userForBase); return jsonObject; } } } //查看个人信息 @CheckToken @GetMapping("/getMessage") public String getMessage() { return "你已通过验证"; } } 最后一步,我们现在来访问一下啊 先进行登录 ![format_png][] 登录后携带token进行业务操作 ![format_png 1][] 简单的Demo程序就到这里了,当然还有很多东西没有考虑到,比如jwt在集群环境下的应用等一些问题留到下回探讨啦~ 其他阅读摘要 文章地址:[https://blog.csdn.net/weixin\_41722928/article/details/101349755][https_blog.csdn.net_weixin_41722928_article_details_101349755] 在令牌有效负载中定义的附加声明可以通过调用getClaims()或 getClaim()和传递声明名来获得。即使无法找到声明,也将会有返回值。您可以通过调用claim.isNull()来检查声明的值是否为null。 Map<String, Claim> claims = jwt.getClaims(); //Key is the Claim name Claim claim = claims.get("isAdmin"); 或者 Claim claim = jwt.getClaim("isAdmin"); 算法定义了一个令牌是如何被签名和验证的。它可以用HMAC算法的原始值来实例化,也可以在RSA和ECDSA算法的情况下对密钥对或密钥提供程序进行实例化。创建后,该实例可用于令牌签名和验证操作。 在使用RSA或ECDSA算法时,只需要签署JWTs,就可以通过传递null值来避免指定公钥。当您需要验证JWTs时,也可以使用私钥进行操作 使用静态的字符密文或者key来获取算法器: //HMAC Algorithm algorithmHS = Algorithm.HMAC256("secret"); //RSA RSAPublicKey publicKey = //Get the key instance RSAPrivateKey privateKey = //Get the key instance Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey); 使用一个key提供者来获取算法: 通过使用KeyProvider,您可以在运行时更改密钥,用于验证令牌签名或为RSA或ECDSA算法签署一个新的令牌。这是通过实现RSAKeyProvider或ECDSAKeyProvider方法实现的: * `getPublicKeyById(String kid)`: 它在令牌签名验证中调用,它应该返回用于验证令牌的密钥。如果使用了关键的轮换,例如JWK,它可以使用id来获取正确的轮换键(或者只是一直返回相同的键)。 * `getPrivateKey()`: 在令牌签名期间调用它,它应该返回用于签署JWT的密钥。 * `getPrivateKeyId()`:在令牌签名期间调用它,它应该返回标识由getPrivateKey()返回的键的id的id。这个值比JWTCreator.Builder和keyid(String)方法中的值更受欢迎。如果您不需要设置孩子的值,就避免使用KeyProvider实例化算法。 [RFC 7519]: https://link.jianshu.com/?t=https://tools.ietf.org/html/rfc7519 [JWT_JAVAWEB]: https://www.cnblogs.com/jimisun/p/9480886.html [format_png]: /images/20221124/5ddd2934581f43499d69009168af876b.png [format_png 1]: /images/20221124/865ee0dcf4bf41819dff1d50f200b737.png [https_blog.csdn.net_weixin_41722928_article_details_101349755]: https://blog.csdn.net/weixin_41722928/article/details/101349755
相关 Java泛型在实际项目中的应用案例分享 泛型是Java中的一种强大特性,它允许我们在创建类或者方法时,使用类型参数来代替具体的类型。在实际项目中,泛型的运用非常广泛。以下是一些案例: 1. 集合类:ArrayLis 矫情吗;*/ 2024年09月17日 11:39/ 0 赞/ 7 阅读
相关 Java反射API在实际项目中的应用问题案例 Java反射API在实际项目中广泛应用,尤其在以下几种场景中问题较为常见: 1. 字符串转对象:有时需要将字符串内容通过反射转换为对应的Java对象。如果处理不当,可能会导致 Love The Way You Lie/ 2024年09月10日 23:45/ 0 赞/ 20 阅读
相关 JWT的原理及实际应用 ![3aedcc68f42e486b9357dfedb20b6609.jpeg][] 前言: 定义:JSON Web Token(缩写 JWT)是目前最流行的跨域认证解 朱雀/ 2024年02月22日 09:30/ 0 赞/ 75 阅读
相关 Jwt在Java项目中的简单实际应用 1.什么是jwt 双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准([RFC 7519][]),定义了一种简洁的,自包含的方法用于通信双 女爷i/ 2022年11月26日 04:00/ 0 赞/ 206 阅读
相关 ActiveMQ在分布式项目中的实际应用 点击关注公众号,利用碎片时间学习![809c45a10b7156f19d8456df3af365fc.png][] 具体需求: 后台添加商品后,需要执行两个操作: 1 清疚/ 2022年09月02日 05:24/ 0 赞/ 169 阅读
相关 java中枚举enum在项目中实际应用 可以把 enum 看成是一个普通的 class,它们都可以定义一些属性和方法,不同之处是:enum 不能使用 extends 关键字继承其他类,因为 enum 已经继承了 ja 红太狼/ 2022年05月12日 20:38/ 0 赞/ 164 阅读
相关 策略模式在实际项目中的应用二 \\\\测试demo git仓库地址:[https://github.com/alwaysInRoad/test-strategy-demo2.git][https_gi 阳光穿透心脏的1/2处/ 2022年05月08日 08:33/ 0 赞/ 212 阅读
相关 策略模式在实际项目中的应用一 \\\\测试demo git仓库地址:[https://github.com/alwaysInRoad/test-strategy-demo1.git][https_gi 亦凉/ 2022年05月08日 06:16/ 0 赞/ 251 阅读
相关 装饰器模式在实际项目中的应用 关于装饰器模式 定义:装饰器模式又名包装(Wrapper)模式。装饰器模式以对客户端透明的方式拓展对象的功能,是继承关系的一种替代方案。 重点:装饰 ゝ一世哀愁。/ 2022年04月08日 11:21/ 0 赞/ 202 阅读
还没有评论,来说两句吧...