springboot-shiro-jwt

1,956 阅读9分钟

项目结构

在这里插入图片描述
在这里插入图片描述

增加全局异常配置

 1/**
2 * @Description GlobaException 全局的异常配置
3 * @Author YiLong Wu
4 * @Date 2020-03-11 22:28
5 * @Version 1.0.0
6 */

7@RestControllerAdvice
8public class GlobalException {
9
10    /**
11     * 处理用户名密码错误的异常
12     * @return
13     */

14    @ExceptionHandler({UnknowUsenameAndPasswordException.class})
15    @ResponseStatus
16    public ResponseError unknowUsenameAndPasswordException() {
17        return new ResponseError(HttpStatus.INTERNAL_SERVER_ERROR.value(),"用户名或密码错误");
18    }
19
20    /**
21     * 处理权限不足的异常
22     * @param e
23     * @return
24     */

25    @ExceptionHandler(AuthorizationException.class)
26    @ResponseStatus(HttpStatus.FORBIDDEN)
27    public ResponseError authorizationException(AuthorizationException e) {
28        return new ResponseError(HttpStatus.FORBIDDEN.value(),"你没有权限访问");
29    }
30
31    /**
32     * 处理token的异常
33     * @return
34     */

35    @ExceptionHandler(InvalidTokenException.class)
36    @ResponseStatus
37    public ResponseError invalidTokenException() {
38        return new ResponseError(HttpStatus.INTERNAL_SERVER_ERROR.value(),"不合法的token");
39    }
40
41    /**
42     * 处理用户账户异常
43     * @return
44     */

45    @ExceptionHandler(UnknownAccountException.class)
46    @ResponseStatus
47    public ResponseError unknownAccountException() {
48        return new ResponseError(HttpStatus.INTERNAL_SERVER_ERROR.value(),"账户异常");
49    }
50}

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

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

 1/**
2 * @ClassName JWTFilter
3 * @Description
4 * @Author yilongwu
5 * @DATE 2020-03-12 17:57
6 * @Version 1.0.0
7 **/

8@Slf4j
9public class JWTFilter extends BasicHttpAuthenticationFilter {
10
11    // 是否允许访问
12    @SneakyThrows
13    @Override
14    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
15        log.info("*********进入isAccessAllowed********");
16        response.setCharacterEncoding("utf-8");
17        response.setContentType("application/json");
18        ResponseError responseError = new ResponseError();
19        if(isLoginAttempt(request,response)) {
20            try {
21                executeLogin(request, response);
22                return  true;
23            } catch (Exception e) {
24                log.info("****InvalidTokenException****");
25               if(e instanceof InvalidTokenException) {
26                   request.getRequestDispatcher("/invalidToken").forward(request,response);
27               }else if(e instanceof UnknownAccountException) {
28                   request.getRequestDispatcher("/unknownAccount").forward(request,response);
29               }
30               return  true;
31            }
32        }
33
34        // 当没有带token访问时
35        responseError.setCode(401);
36        responseError.setMessage("没有访问凭证");
37        ObjectMapper objectMapper = new ObjectMapper();
38        response.getWriter().write(objectMapper.writeValueAsString(responseError));
39        return false;
40    }
41
42    // 执行登录
43    @Override
44    protected boolean executeLogin(ServletRequest request, ServletResponse response)  {
45        log.info("************进入executeLogin*******");
46        String token = ((HttpServletRequest) request).getHeader("Authorization");
47                // 自定义的认证token
48        JWTToken jwtToken = new JWTToken(token);
49        getSubject(request,response).login(jwtToken);
50        return true;
51    }
52
53    // 是否接受登录
54    @Override
55    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
56        log.info("*******************进入isLoginAttempt********");
57        String token = ((HttpServletRequest) request).getHeader("Authorization");
58        return StringUtils.isNotBlank(token) && token.startsWith("Bearer ");
59    }
60}

shiro结合jwt认证异常问题

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

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

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

 1  public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
2
3        if (token == null) {
4            throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
5        }
6
7        log.trace("Authentication attempt received for token [{}]", token);
8
9        AuthenticationInfo info;
10        try {
11            info = doAuthenticate(token);
12            if (info == null) {
13                String msg = "No account information found for authentication token [" + token + "] by this " +
14   "Authenticator instance.  Please check that it is configured correctly.";
15                throw new AuthenticationException(msg);
16            }
17        } catch (Throwable t) {
18            AuthenticationException ae = null;
19            if (t instanceof AuthenticationException) {
20                ae = (AuthenticationException) t;
21            }
22            if (ae == null) {
23                //Exception thrown was not an expected AuthenticationException.  Therefore it is probably a little more
24                //severe or unexpected.  So, wrap in an AuthenticationException, log to warn, and propagate:
25                String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected " +
26                        "error? (Typical or expected login exceptions should extend from AuthenticationException).";
27                ae = new AuthenticationException(msg, t);
28                if (log.isWarnEnabled())
29                    log.warn(msg, t);
30            }
31            try {
32                notifyFailure(token, ae);
33            } catch (Throwable t2) {
34                if (log.isWarnEnabled()) {
35                    String msg = "Unable to send notification for failed 
36                    authentication attempt - listener error?.  "
 +
37                            "Please check your AuthenticationListener implementation(s).  Logging sending exception " +
38                            "and propagating original AuthenticationException instead...";
39                    log.warn(msg, t2);
40                }
41            }
42
43
44            throw ae;
45        }
46
47        log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);
48
49        notifySuccess(token, info);
50
51        return info;
52    }

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

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

 1 if(isLoginAttempt(request,response)) {
2            try {
3                boolean b = executeLogin(request, response);
4                return  true;
5            } catch (Exception e) {
6                log.info("****InvalidTokenException");
7               if(e instanceof InvalidTokenException) {
8                   request.setAttribute("invalidToken","不合法的token");
9                   request.getRequestDispatcher("/invalidToken").forward(request,response);
10                   return  true;
11               }
12
13            }
14        }

最后

项目的github地址:springboot-shiro-jwt