Spring Security及Shiro整合JWT 网上教程的一些坑!

今日主要在搭建一个新的项目,原先项目已采用Shiro JWT的方式实现登陆,为了丰富新项目权限控制的功能,采用Spring Security进行权限控制。希望2套系统通过JWT达到单点登陆的目的。由于项目规模过小,以快速开发为目的,所以不采用统一的auth服务器。但在进行构建时候,参考网上一些教程,发现网上常见的教程存在诸多问题。大多数文章都是copy同一篇文章,问题一直遗留。所以决定写一下这篇文章供大家参考。如果有错误的地方,希望大家能给我指正。

JWT介绍

什么是JWT

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,用于在各方之间作为JSON对象安全地传输信息。

JWT结构格式

JSON Web Tokens由点(.)分隔的三个部分组成,它们是:

1.Header:头信息通常由两部分组成:令牌的类型,即JWT,以及正在使用的签名算法,例如HMAC SHA256或RSA。

{"alg": "HS256",//签名或摘要算法"typ": "JWT"//token类型
} 

2.Playload 有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。

{"iss": "token-server",//签发者"exp ": "Mon Nov 13 15:28:41 CST 2017",//过期时间"sub ": "wangjie",//用户名"aud": "web-server-1"//接收方,"nbf": "Mon Nov 13 15:40:12 CST 2017",//这个时间之前token不可用"jat": "Mon Nov 13 15:20:41 CST 2017",//签发时间"jti": "0023",//令牌id标识"claim": {“auth”:”ROLE_ADMIN”}//访问主张
} 

3.Signature 签名信息:为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然对它们签名即可。

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) 

JSON Web令牌如何工作?

每当用户想要访问受保护的路由或资源时,用户代理应该使用承载模式发送JWT,通常在Authorization标头中。标题的内容应如下所示:

Authorization: Bearer <token> 

在某些情况下,这可以是无状态授权机制。服务器的受保护路由将检查Authorization标头中的有效JWT ,如果存在,则允许用户访问受保护资源。如果JWT包含必要的数据,则可以减少查询数据库以进行某些操作的需要,尽管可能并非总是如此。

Shiro整合JWT教程

教材原帖地址:juejin.cn/post/684490…

问题指正1

public class JWTUtil {问题代码/** * 校验token是否正确 * @param token 密钥 * @param secret 用户的密码 * @return 是否正确 */public static boolean verify(String token, String username, String secret) {try {Algorithm algorithm = Algorithm.HMAC256(secret);JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();DecodedJWT jwt = verifier.verify(token);return true;} catch (Exception exception) {return false;}}修订后代码token中已经包含Playload信息(username),此处进行校验不需要再传入usernamepublic static boolean verify(String token, String username, String secret) {try {Algorithm algorithm = Algorithm.HMAC256(secret);JWTVerifier verifier = JWT.require(algorithm).build();DecodedJWT jwt = verifier.verify(token);return true;} catch (Exception exception) {return false;}} 

问题2 @RestControllerAdvice无法捕获Shiro异常

public class JWTFilter extends BasicHttpAuthenticationFilter {/** * 将非法请求返回json */@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {HttpServletResponse httpResponse = WebUtils.toHttp(response);httpResponse.setCharacterEncoding("UTF-8");httpResponse.setContentType("application/json;charset=utf-8");httpResponse.setStatus(HttpServletResponse.SC_OK);httpResponse.getWriter().write("{\"code\":401,\"msg\":\"未授权!\",\"ret\":false}");return false;}
} 

Spring Security整合JWT

教材原帖地址:juejin.cn/post/684490… (PS:绝大多数教材和该贴类似)

问题代码

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws ServletException, IOException {String authHeader = request.getHeader(this.tokenHeader);if (authHeader != null && authHeader.startsWith(tokenHead)) {final String authToken = authHeader.substring(tokenHead.length()); // The part after "Bearer "String username = jwtTokenUtil.getUsernameFromToken(authToken);logger.info("checking authentication " + username);if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);//重点观察此处 从token中得到了用户名,再通过用户名从数据库中获取用户的详细信息//接下来我们看一下jwtTokenUtil.validateToken()到底做了什么事if (jwtTokenUtil.validateToken(authToken, userDetails)) {UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));logger.info("authenticated user " + username + ", setting security context");SecurityContextHolder.getContext().setAuthentication(authentication);}}}chain.doFilter(request, response);}
}

@Component
public class JwtTokenUtil implements Serializable {/** * 验证令牌 */public Boolean validateToken(String token, UserDetails userDetails) {JwtUser user = (JwtUser) userDetails;String username = getUsernameFromToken(token);//这里我看得一脸懵逼 不应该是校验token的合法性。//这里居然用数据库中查到的username 和token的username比较。而数据中查询的username又是通过token的来的。//所以这段代码有错误return (username.equals(user.getUsername()) && !isTokenExpired(token));}
} 

一些疑问

1.com.auth0/java-jwt 3.3.0和 io.jsonwebtoken / jjwt / 0.9.0 确保token中的Header,Playload(连参数顺序都一样)但是2个包获得的token不能认证通过。 目前我的做法是 全部替换为com.auth0/java-jwt来使用

JWT官网介绍

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值