0%

springboot结合shiro和jwt

项目结构

在这里插入图片描述

增加全局异常配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* @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过滤器用于认证过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
* @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,要不在自定义全局异常时控制台会出现

1
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进去看到如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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-jwt

-------------本文结束感谢你的阅读---------