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

栏目: Java · 发布时间: 5年前

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

今日主要在搭建一个新的项目,原先项目已采用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类型
}
复制代码
  1. 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”}//访问主张
}
复制代码
  1. Signature 签名信息:为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然对它们签名即可。
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
复制代码

JSON Web令牌如何工作?

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

Authorization: Bearer <token>
复制代码

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

Shiro整合JWT教程

教材原帖地址: juejin.im/post/59f1b2…

问题指正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),此处进行校验不需要再传入username
    public 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
     */
    @Override
    protected 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.im/post/58c29e… (PS:绝大多数教材和该贴类似)

问题代码

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

  
    @Override
    protected 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));
    }
}
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

汇编语言(第3版)

汇编语言(第3版)

王爽 / 清华大学出版社 / 2013-9 / 36.00元

《汇编语言(第3版)》具有如下特点:采用了全新的结构对课程的内容进行组织,对知识进行最小化分割,为读者构造了循序渐进的学习线索;在深入本质的层面上对汇编语言进行讲解;对关键环节进行深入的剖析。《汇编语言(第3版)》可用作大学计算机专业本科生的汇编教材及希望深入学习计算机科学的读者的自学教材。一起来看看 《汇编语言(第3版)》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试