springboot结合shiro和jwt

WuYiLong原创大约 3 分钟javashirojwt

项目结构

在这里插入图片描述

增加全局异常配置

/**
 * @Description GlobaException 全局的异常配置
 * @Author YiLong Wu
 * @Date 2020-03-11 22:28
 * @Version 1.0.0
 */
@RestControllerAdvice
public class GlobalException {

    /**
     * 处理用户名密码错误的异常
     * @return
     */
    @ExceptionHandler({UnknowUsenameAndPasswordException.class})
    @ResponseStatus
    public ResponseError unknowUsenameAndPasswordException() {
        return new ResponseError(HttpStatus.INTERNAL_SERVER_ERROR.value(),"用户名或密码错误");
    }

    /**
     * 处理权限不足的异常
     * @param e
     * @return
     */
    @ExceptionHandler(AuthorizationException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public ResponseError authorizationException(AuthorizationException e) {
        return new ResponseError(HttpStatus.FORBIDDEN.value(),"你没有权限访问");
    }

    /**
     * 处理token的异常
     * @return
     */
    @ExceptionHandler(InvalidTokenException.class)
    @ResponseStatus
    public ResponseError invalidTokenException() {
        return new ResponseError(HttpStatus.INTERNAL_SERVER_ERROR.value(),"不合法的token");
    }

    /**
     * 处理用户账户异常
     * @return
     */
    @ExceptionHandler(UnknownAccountException.class)
    @ResponseStatus
    public ResponseError unknownAccountException() {
        return new ResponseError(HttpStatus.INTERNAL_SERVER_ERROR.value(),"账户异常");
    }
}

相关解析: @RestControllerAdvice要结合ExceptionHandler一起使用,增加@ResponseStatus,是为了更友好的响应给客户端

增加JwtFilter过滤器用于认证过滤

/**
 * @ClassName JWTFilter
 * @Description
 * @Author yilongwu
 * @DATE 2020-03-12 17:57
 * @Version 1.0.0
 **/
@Slf4j
public class JWTFilter extends BasicHttpAuthenticationFilter {

    // 是否允许访问
    @SneakyThrows
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        log.info("*********进入isAccessAllowed********");
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json");
        ResponseError responseError = new ResponseError();
        if(isLoginAttempt(request,response)) {
            try {
                executeLogin(request, response);
                return  true;
            } catch (Exception e) {
                log.info("****InvalidTokenException****");
               if(e instanceof InvalidTokenException) {
                   request.getRequestDispatcher("/invalidToken").forward(request,response);
               }else if(e instanceof UnknownAccountException) {
                   request.getRequestDispatcher("/unknownAccount").forward(request,response);
               }
               return  true;
            }
        }

        // 当没有带token访问时
        responseError.setCode(401);
        responseError.setMessage("没有访问凭证");
        ObjectMapper objectMapper = new ObjectMapper();
        response.getWriter().write(objectMapper.writeValueAsString(responseError));
        return false;
    }

    // 执行登录
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response)  {
        log.info("************进入executeLogin*******");
        String token = ((HttpServletRequest) request).getHeader("Authorization");
				// 自定义的认证token
        JWTToken jwtToken = new JWTToken(token);
        getSubject(request,response).login(jwtToken);
        return true;
    }

    // 是否接受登录
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        log.info("*******************进入isLoginAttempt********");
        String token = ((HttpServletRequest) request).getHeader("Authorization");
        return StringUtils.isNotBlank(token) && token.startsWith("Bearer ");
    }
}

shiro结合jwt认证异常问题

自定义异常要继承AuthenticationException,要不在自定义全局异常时控制台会出现

Authentication failed for token submission [JWTToken(token=Bearer eyJhbGciOiJIUzUxMiJ9.eyJuYW1lIjoiYWRtaW4iLCJleHAiOjE1ODQ0MjU2ODd9.7qqO9TjI8RbS12FELNI8n5OcnQABezfv6AtbbbZDGy7bwYfu3PH1r9RKOHVEBlLarI7w47QhU-kmm8GON0g6n)].  Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException).

为什么会出现?在认证抛出异常的地方debug进去看到如下

  public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {

        if (token == null) {
            throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
        }

        log.trace("Authentication attempt received for token [{}]", token);

        AuthenticationInfo info;
        try {
            info = doAuthenticate(token);
            if (info == null) {
                String msg = "No account information found for authentication token [" + token + "] by this " +
   "Authenticator instance.  Please check that it is configured correctly.";
                throw new AuthenticationException(msg);
            }
        } catch (Throwable t) {
            AuthenticationException ae = null;
            if (t instanceof AuthenticationException) {
                ae = (AuthenticationException) t;
            }
            if (ae == null) {
                //Exception thrown was not an expected AuthenticationException.  Therefore it is probably a little more
                //severe or unexpected.  So, wrap in an AuthenticationException, log to warn, and propagate:
                String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected " +
                        "error? (Typical or expected login exceptions should extend from AuthenticationException).";
                ae = new AuthenticationException(msg, t);
                if (log.isWarnEnabled())
                    log.warn(msg, t);
            }
            try {
                notifyFailure(token, ae);
            } catch (Throwable t2) {
                if (log.isWarnEnabled()) {
                    String msg = "Unable to send notification for failed 
                    authentication attempt - listener error?.  " +
                            "Please check your AuthenticationListener implementation(s).  Logging sending exception " +
                            "and propagating original AuthenticationException instead...";
                    log.warn(msg, t2);
                }
            }


            throw ae;
        }

        log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);

        notifySuccess(token, info);

        return info;
    }

很明显就看到了如果抛出的异常不是AuthenticationException的子类,那么就会出现这个消息

还有就是这个时候下面的代码捕获的异常已经变成AuthenticationException,所以会无法dispatcher.

 if(isLoginAttempt(request,response)) {
            try {
                boolean b = executeLogin(request, response);
                return  true;
            } catch (Exception e) {
                log.info("****InvalidTokenException");
               if(e instanceof InvalidTokenException) {
                   request.setAttribute("invalidToken","不合法的token");
                   request.getRequestDispatcher("/invalidToken").forward(request,response);
                   return  true;
               }

            }
        }

最后

项目的github地址:springboot-shiro-jwtopen in new window

上次编辑于:
贡献者: wuyilong