Skip to content

Commit

Permalink
添加Shiro自带拦截器配置规则注释,代码规范调整
Browse files Browse the repository at this point in the history
  • Loading branch information
dolyw committed Apr 16, 2019
1 parent c634f5c commit 55f76bf
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 47 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ Token的自动刷新也是在Token失效时返回新的Token在Header信息的Au
6. 感谢yaxx的Vuejs之axios获取Http响应头:[https://segmentfault.com/a/1190000009125333](https://segmentfault.com/a/1190000009125333)
7. 感谢Twilight的解决使用jwt刷新token带来的问题:[https://segmentfault.com/a/1190000013151506](https://segmentfault.com/a/1190000013151506)
8. 感谢chuhx的shiro拦截器,返回json数据:[https://blog.csdn.net/chuhx/article/details/51148877](https://blog.csdn.net/chuhx/article/details/51148877)
9. 感谢yidao620c的Shiro自带拦截器配置规则:[https://github.com/yidao620c/SpringBootBucket/tree/master/springboot-jwt](https://github.com/yidao620c/SpringBootBucket/tree/master/springboot-jwt)

#### 参与贡献

Expand Down
32 changes: 25 additions & 7 deletions src/main/java/com/wang/config/shiro/ShiroConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,41 @@ public class ShiroConfig {
*/
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Bean("securityManager")
public DefaultWebSecurityManager getManager(UserRealm userRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
public DefaultWebSecurityManager defaultWebSecurityManager(UserRealm userRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 使用自定义Realm
manager.setRealm(userRealm);
defaultWebSecurityManager.setRealm(userRealm);
// 关闭Shiro自带的session
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
manager.setSubjectDAO(subjectDAO);
defaultWebSecurityManager.setSubjectDAO(subjectDAO);
// 设置自定义Cache缓存
manager.setCacheManager(new CustomCacheManager());
return manager;
defaultWebSecurityManager.setCacheManager(new CustomCacheManager());
return defaultWebSecurityManager;
}

/**
* 添加自己的过滤器,自定义url规则
* Shiro自带拦截器配置规则
* rest:比如/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等
* port:比如/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数
* perms:比如/admins/user/**=perms[user:add:*],perms参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,比如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法
* roles:比如/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,比如/admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。//要实现or的效果看http://zgzty.blog.163.com/blog/static/83831226201302983358670/
* anon:比如/admins/**=anon 没有参数,表示可以匿名使用
* authc:比如/admins/user/**=authc表示需要认证才能使用,没有参数
* authcBasic:比如/admins/user/**=authcBasic没有参数表示httpBasic认证
* ssl:比如/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
* user:比如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
* 详情见文档 http://shiro.apache.org/web.html#urls-
* @param securityManager
* @return org.apache.shiro.spring.web.ShiroFilterFactoryBean
* @author Wang926454
* @date 2018/8/31 10:57
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager) {
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 添加自己的过滤器取名为jwt
Map<String, Filter> filterMap = new HashMap<>(16);
Expand All @@ -69,6 +79,14 @@ public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager)
factoryBean.setSecurityManager(securityManager);
// 自定义url规则使用LinkedHashMap有序Map
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(16);
// Swagger接口文档
// filterChainDefinitionMap.put("/v2/api-docs", "anon");
// filterChainDefinitionMap.put("/webjars/**", "anon");
// filterChainDefinitionMap.put("/swagger-resources/**", "anon");
// filterChainDefinitionMap.put("/swagger-ui.html", "anon");
// filterChainDefinitionMap.put("/doc.html", "anon");
// 公开接口
// filterChainDefinitionMap.put("/api/**", "anon");
// 所有请求通过我们自己的JWTFilter
filterChainDefinitionMap.put("/**", "jwt");
factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/com/wang/config/shiro/UserRealm.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ public UserRealm(UserMapper userMapper, RoleMapper roleMapper, PermissionMapper
* 大坑,必须重写此方法,不然Shiro会报错
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
public boolean supports(AuthenticationToken authenticationToken) {
return authenticationToken instanceof JwtToken;
}

/**
* 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
String account = JwtUtil.getClaim(principals.toString(), Constant.ACCOUNT);
String account = JwtUtil.getClaim(principalCollection.toString(), Constant.ACCOUNT);
UserDto userDto = new UserDto();
userDto.setAccount(account);
// 查询用户角色
Expand All @@ -83,8 +83,8 @@ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal
* 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
String token = (String) auth.getCredentials();
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String token = (String) authenticationToken.getCredentials();
// 解密获得account,用于和数据库进行对比
String account = JwtUtil.getClaim(token, Constant.ACCOUNT);
// 帐号为空
Expand Down
53 changes: 26 additions & 27 deletions src/main/java/com/wang/config/shiro/jwt/JwtFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,21 @@ protected boolean isAccessAllowed(ServletRequest request, ServletResponse respon
一般使用第二种(更方便)
*/
// 直接返回Response信息
this.response401(request, response, msg);
this.response401(response, msg);
return false;
}
} else {
// 没有携带Token
HttpServletRequest httpRequest = WebUtils.toHttp(request);
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
// 获取当前请求类型
String httpMethod = httpRequest.getMethod();
String httpMethod = httpServletRequest.getMethod();
// 获取当前请求URI
String requestURI = httpRequest.getRequestURI();
String requestURI = httpServletRequest.getRequestURI();
LOGGER.info("当前请求 {} Authorization属性(Token)为空 请求类型 {}", requestURI, httpMethod);

// mustLoginFlag = true 开启任何请求必须登录才可访问
Boolean mustLoginFlag = false;
if (mustLoginFlag) {
this.response401(request, response, "请先登录");
this.response401(response, "请先登录");
return false;
}
}
Expand Down Expand Up @@ -135,6 +134,24 @@ protected boolean executeLogin(ServletRequest request, ServletResponse response)
return true;
}

/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}

/**
* 此处为AccessToken刷新,进行判断RefreshToken是否过期,未过期就返回新的AccessToken且继续正常访问
*/
Expand Down Expand Up @@ -163,7 +180,7 @@ private boolean refreshToken(ServletRequest request, ServletResponse response) {
// 提交给UserRealm进行认证,如果错误他会抛出异常并被捕获,如果没有抛出异常则代表登入成功,返回true
this.getSubject(request, response).login(jwtToken);
// 最后将刷新的AccessToken存放在Response的Header中的Authorization字段返回
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
httpServletResponse.setHeader("Authorization", token);
httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
return true;
Expand All @@ -175,8 +192,8 @@ private boolean refreshToken(ServletRequest request, ServletResponse response) {
/**
* 无需转发,直接返回Response信息
*/
private void response401(ServletRequest req, ServletResponse resp, String msg) {
HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
private void response401(ServletResponse response, String msg) {
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json; charset=utf-8");
Expand All @@ -188,22 +205,4 @@ private void response401(ServletRequest req, ServletResponse resp, String msg) {
throw new CustomException("直接返回Response信息出现IOException异常:" + e.getMessage());
}
}

/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
}
14 changes: 7 additions & 7 deletions src/main/java/com/wang/util/AesCipherUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ public static String enCrypto(String str) {
secureRandom.setSeed(Base64ConvertUtil.decode(encryptAESKey).getBytes());
keygen.init(128, secureRandom);
// SecretKey 负责保存对称密钥 生成密钥
SecretKey deskey = keygen.generateKey();
SecretKey desKey = keygen.generateKey();
// 生成Cipher对象,指定其支持的AES算法,Cipher负责完成加密或解密工作
Cipher c = Cipher.getInstance("AES");
// 根据密钥,对Cipher对象进行初始化,ENCRYPT_MODE表示加密模式
c.init(Cipher.ENCRYPT_MODE, deskey);
c.init(Cipher.ENCRYPT_MODE, desKey);
byte[] src = str.getBytes();
// 该字节数组负责保存加密的结果
byte[] cipherByte = c.doFinal(src);
Expand Down Expand Up @@ -100,20 +100,20 @@ public static String deCrypto(String str) {
secureRandom.setSeed(Base64ConvertUtil.decode(encryptAESKey).getBytes());
keygen.init(128, secureRandom);
// SecretKey 负责保存对称密钥 生成密钥
SecretKey deskey = keygen.generateKey();
SecretKey desKey = keygen.generateKey();
// 生成Cipher对象,指定其支持的AES算法,Cipher负责完成加密或解密工作
Cipher c = Cipher.getInstance("AES");
// 根据密钥,对Cipher对象进行初始化,DECRYPT_MODE表示解密模式
c.init(Cipher.DECRYPT_MODE, deskey);
// 该字节数组负责保存加密的结果,先对str进行Base64解密,将16进制转换为二进制
c.init(Cipher.DECRYPT_MODE, desKey);
// 该字节数组负责保存解密的结果,先对str进行Base64解密,将16进制转换为二进制
byte[] cipherByte = c.doFinal(HexConvertUtil.parseHexStr2Byte(Base64ConvertUtil.decode(str)));
return new String(cipherByte);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
LOGGER.error("getInstance()方法异常:" + e.getMessage());
throw new CustomUnauthorizedException("getInstance()方法异常:" + e.getMessage());
} catch (UnsupportedEncodingException e) {
LOGGER.error("Base64加密异常:" + e.getMessage());
throw new CustomUnauthorizedException("Base64加密异常:" + e.getMessage());
LOGGER.error("Base64解密异常:" + e.getMessage());
throw new CustomUnauthorizedException("Base64解密异常:" + e.getMessage());
} catch (InvalidKeyException e) {
LOGGER.error("初始化Cipher对象异常:" + e.getMessage());
throw new CustomUnauthorizedException("初始化Cipher对象异常:" + e.getMessage());
Expand Down

0 comments on commit 55f76bf

Please sign in to comment.