此文集中3个点
- 怎么去实现验证token的拦截器
- 如何生成token
怎么去实现验证token的拦截器
查看shiro的文档 关于其拦截器https://www.w3cschool.cn/shiro/oibf1ifh.html
重写shiro的逻辑实现功能

主要重写方法isAccessAllowed 所有请求将会进入此方法
参考文章https://blog.csdn.net/qq_33591200/article/details/82884304

shiro的拦截器管理方法 shiroFilter
加入代码
1 2
| filterChainMap.put("/*", "authcBasic");
|
拦截器实现流程:
对BasicHttpAuthenticationFilter进行重写得到需要的效果
在isAccessAllowed里 如果request里的头不带token 对其进行放行,后期会加上对指定url放行;
在login登录中 验证成功即创建一个token 返回给前端
前端获得正常返回值后 请求头都会带着token 前端代码就不放了 自行百度
前端在验证成功马上就会去请求用户数据 此时带着token进行请求
在拦截器isAccessAllowed 进入 验证方法 executeLogin
此时使用 subject.login时 使用自定义createtoken创建一个自定义AuthenticationToken
而login方法要进入自定义realm进行处理 所以我们需要 新增一个JwtRealm 对带着token的请求进行验证处理
重写onLoginSuccess 当token验证成功后 对token进行刷新
大致的就是这样
关于jwt的maven
1 2 3 4 5 6
| <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.2.0</version> </dependency>
|
关于shiro快速整合jwt可以尝试https://github.com/louislivi/fastdep
shiro配置类
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
| package com.xia.zhb.shiro;
import com.xia.zhb.shiro.filter.JwtAuthTokenFilter; import com.xia.zhb.shiro.realm.JwtRealm; import com.xia.zhb.shiro.realm.MyRealm; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authc.pam.AuthenticationStrategy; import org.apache.shiro.authc.pam.FirstSuccessfulStrategy; import org.apache.shiro.authc.pam.ModularRealmAuthenticator; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter; import java.util.*;
@Slf4j @Configuration public class ShiroConfig {
@Bean public MyRealm myAuthRealm() { MyRealm myRealm = new MyRealm(); log.info("====myRealm注册完成====="); return myRealm; }
@Bean public JwtRealm jwtAuthRealm() { JwtRealm jwtRealm = new JwtRealm(); log.info("====jwtRealm注册完成====="); return jwtRealm; }
@Bean public ModularRealmAuthenticator authenticator() { ModularRealmAuthenticator authenticator = new MultiRealmAuthenticator(); AuthenticationStrategy strategy = new FirstSuccessfulStrategy(); authenticator.setAuthenticationStrategy(strategy); return authenticator; }
@Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(myAuthRealm()); securityManager.setAuthenticator(authenticator());
List<Realm> realms = new ArrayList<Realm>(16); realms.add(jwtAuthRealm()); realms.add(myAuthRealm()); securityManager.setRealms(realms);
log.info("====securityManager注册完成===="); return securityManager; }
@Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setSuccessUrl("/success"); shiroFilterFactoryBean.setUnauthorizedUrl("/error");
Map<String,String> filterChainMap = new LinkedHashMap<String,String>();
Map<String, Filter> filterMap = new HashMap<String, Filter>(1); filterMap.put("authcBasic", new JwtAuthTokenFilter());
shiroFilterFactoryBean.setFilters(filterMap);
filterChainMap.put("/static/static/**", "anon"); filterChainMap.put("/static/**", "anon"); filterChainMap.put("/swagger-*/**", "anon"); filterChainMap.put("/swagger-ui.html/**", "anon"); filterChainMap.put("/prod-api/captcha", "anon"); filterChainMap.put("/captcha", "anon");
filterChainMap.put("/*", "authcBasic"); filterChainMap.put("/**", "anon"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap); log.info("====shiroFilterFactoryBean注册完成===="); return shiroFilterFactoryBean; }
}
|
拦截器代码
JwtAuthTokenFilter
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
| package com.xia.zhb.shiro.filter; import com.xia.zhb.controller.common.domain.AjaxResult; import com.xia.zhb.shiro.JWTToken; import com.xia.zhb.utils.JwtUtil; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; import org.apache.shiro.web.util.WebUtils; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component;
import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter;
@Slf4j @Component public class JwtAuthTokenFilter extends BasicHttpAuthenticationFilter {
@Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
String token=JwtUtil.getToken(request);
log.info("token->"+ token);
if (token != null) {
try {
executeLogin(request, response); return true; } catch (Exception e) { e.printStackTrace(); }
} return true; } @Override protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
AuthenticationToken token = createToken(request, response);
try { Subject subject = getSubject(request, response); subject.login(token); return onLoginSuccess(token, subject, request, response); } catch (AuthenticationException e) { return onLoginFailure(token, e, request, response); }
}
@Override protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) {
String token = JwtUtil.getToken(servletRequest); JWTToken jwtToken = new JWTToken(token); return jwtToken; }
@Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { HttpServletResponse httpResponse = WebUtils.toHttp(response); String newToken = null; if (token instanceof JWTToken) { newToken = JwtUtil.refreshTokenExpired((String) token.getCredentials()); } if (newToken != null) httpResponse.setHeader(JwtUtil.header, newToken); return true; }
@Override protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { return false; }
@Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse httpResponse = WebUtils.toHttp(response); httpResponse.setCharacterEncoding("UTF-8"); httpResponse.setContentType("application/json;charset=UTF-8"); httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); PrintWriter writer = httpResponse.getWriter(); AjaxResult ajaxResult=AjaxResult.error("token不合法"); writer.write(ajaxResult.toString());
return false;
} private void authenticationFail(ServletRequest req, ServletResponse resp) { try { String loginUrl = getLoginUrl(); WebUtils.issueRedirect(req, resp, loginUrl); } catch (IOException e) { log.error(e.getMessage()); } }
}
|
通过重写createToken方法 在处理非login请求被拦截时 构建一个自定义的token
JWTToken
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
| package com.xia.zhb.shiro;
import lombok.Data;
import org.apache.shiro.authc.AuthenticationToken; @Data public class JWTToken implements AuthenticationToken {
private String token;
private String username;
private char[] password;
public JWTToken(String token) { this.token = token; }
@Override public Object getPrincipal() { return username; }
@Override public Object getCredentials() { return token; }
}
|
如何生成token
结合jwt创建token
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
| package com.xia.zhb.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator.Builder; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTCreationException; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; import com.xia.zhb.controller.common.Constants; import com.xia.zhb.controller.common.StringUtils; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.web.util.WebUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;
import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.HashMap; import java.util.Map;
import java.util.Map.Entry;
@Slf4j @Component public class JwtUtil { public static String tokenExpireTime;
public static String secret;
public static String header;
@Value("${token.expireTime}") public void setTokenExpireTime(String tokenExpireTime) { JwtUtil.tokenExpireTime=tokenExpireTime; }
@Value("${token.secret}") public void setSecret(String secret) { JwtUtil.secret=secret; }
@Value("${token.header}") public void setHeader(String header) { JwtUtil.header=header; }
public static boolean verify(String token, String username) {
try { Algorithm algorithm = Algorithm.HMAC256(secret); JWTVerifier verifier = JWT.require(algorithm) .withClaim("userName", username) .build(); DecodedJWT jwt = verifier.verify(token);
} catch (Exception exception) {
return false; } return true; }
public static String getToken(ServletRequest request) { HttpServletRequest httpRequest = WebUtils.toHttp(request); String token=null; try { token = httpRequest.getHeader(header); if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) { token = token.replace(Constants.TOKEN_PREFIX, ""); } }catch (NullPointerException e) { log.info("无token"); return null; }
return token;
}
public static String getUsername(String token) { try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("userName").asString(); } catch (JWTDecodeException e) { e.printStackTrace(); return null; } }
public static String sign(String username) { try { Date date = new Date(System.currentTimeMillis() + Integer.parseInt(tokenExpireTime) * 60 * 1000);
Algorithm algorithm = Algorithm.HMAC256(secret); return JWT.create() .withClaim("userName", username) .withExpiresAt(date) .sign(algorithm); } catch (Exception e) { e.printStackTrace(); return null; } }
public static String refreshTokenExpired(String token) { DecodedJWT jwt = JWT.decode(token); Map<String, Claim> claims = jwt.getClaims(); try { Date date = new Date(System.currentTimeMillis() + Integer.parseInt(tokenExpireTime) * 60 * 1000); Algorithm algorithm = Algorithm.HMAC256(secret); Builder builer = JWT.create().withExpiresAt(date); for (Entry<String, Claim> entry : claims.entrySet()) { builer.withClaim(entry.getKey(), entry.getValue().asString()); } return builer.sign(algorithm); } catch (Exception e) { return null; } }
}
|
新增处理token的realm JwtRealm
JwtRealm
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| package com.xia.zhb.shiro.realm;
import com.xia.zhb.shiro.JWTToken; import com.xia.zhb.entity.SysUser; import com.xia.zhb.service.SysUserService; import com.xia.zhb.utils.JwtUtil; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource;
import javax.annotation.Resource; import java.util.HashSet; import java.util.Set;
public class JwtRealm extends AuthorizingRealm {
@Resource private SysUserService sysUserService;
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String username = (String) principalCollection.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Set<String> setrole = new HashSet<String>(); setrole.add(sysUserService.queryUsernameToRole(username)); authorizationInfo.setRoles(setrole); Set<String> setpermission = new HashSet<String>(); setpermission.add(sysUserService.queryUsernameToPermission(username)); authorizationInfo.setStringPermissions(setpermission); return authorizationInfo; }
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { JWTToken jwtToken=(JWTToken)authenticationToken; String token=(String) jwtToken.getCredentials(); String username=JwtUtil.getUsername(token);
if (JwtUtil.verify(token,username )) { SysUser user = sysUserService.queryUsername(username); if (user != null) { ByteSource credentialsSalt = ByteSource.Util.bytes(username); return new SimpleAuthenticationInfo(user, user.getUserName(), getName());
} else { throw new AuthenticationException("该用户不存在!"); } }else{ throw new AuthenticationException("非法token!"); } }
@Override public boolean supports(AuthenticationToken token) { return token instanceof JWTToken; }
}
|
为了让其生效 请看shiroconfig代码
处理自定义token报错 重写ModularRealmAuthenticator
MultiRealmAuthenticator
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 61 62
| package com.xia.zhb.shiro;
import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.pam.AuthenticationStrategy; import org.apache.shiro.authc.pam.ModularRealmAuthenticator; import org.apache.shiro.realm.Realm;
import java.util.Collection;
@Slf4j public class MultiRealmAuthenticator extends ModularRealmAuthenticator {
@Override protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) throws AuthenticationException { AuthenticationStrategy strategy = getAuthenticationStrategy();
AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);
if (log.isTraceEnabled()) { log.trace("Iterating through {} realms for PAM authentication", realms.size()); } AuthenticationException authenticationException = null; for (Realm realm : realms) {
aggregate = strategy.beforeAttempt(realm, token, aggregate);
if (realm.supports(token)) {
log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);
AuthenticationInfo info = null; try { info = realm.getAuthenticationInfo(token); } catch (AuthenticationException e) { authenticationException = e; if (log.isDebugEnabled()) { String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:"; log.debug(msg, e); } }
aggregate = strategy.afterAttempt(realm, token, info, aggregate, authenticationException);
} else { log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token); } } if(authenticationException != null){ throw authenticationException; } aggregate = strategy.afterAllAttempts(token, aggregate);
return aggregate;
} }
|
完成这些后不带token的login登录就进入了自定义Realm MyRealm 进行验证
而带token进入JwtRealm进行验证
参考:
https://blog.csdn.net/z237794193/article/details/91046302
https://www.jianshu.com/p/3c51832f1051
https://blog.csdn.net/pengjunlee/article/details/95600843