参考:[基于shiro的改造集成真正支持restful请求] (https://segmentfault.com/a/1190000014545172)
在之前的springboot+shiro中无法拦截restful请求,比如
post:user/tom 和 get:user/tom 两个url是相同的,但是请求方式不同。所以要对shiro改造
这里自定义了一些规则: shiro过滤器链的url=url+"=="+httpMethod
eg:对于url="api/resource/",httpMethod="GET"的资源,其拼接出来的过滤器链匹配url=api/resource==GET ,这样对相同的url而不同的访问方式,会判定为不同的资源,即资源不再简单是url,而是url和httpMethod的组合。
重写PathMatchingFilterChainResolver的getChain方法,改变过滤器的过滤匹配url规则,增加对上述规则的url的支持。
public class RestPathMatchingFilterChainResolver extends PathMatchingFilterChainResolver {
private static final transient Logger log = LoggerFactory.getLogger(PathMatchingFilterChainResolver.class);
public RestPathMatchingFilterChainResolver() {
super();
}
public RestPathMatchingFilterChainResolver(FilterConfig filterConfig) {
super(filterConfig);
}
/* *
* @Description 重写filterChain匹配
* @Param [request, response, originalChain]
* @Return javax.servlet.FilterChain
*/
@Override
public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
FilterChainManager filterChainManager = this.getFilterChainManager();
if (!filterChainManager.hasChains()) {
return null;
} else {
String requestURI = this.getPathWithinApplication(request);
Iterator var6 = filterChainManager.getChainNames().iterator();
String pathPattern;
boolean flag = true;
String[] strings = null;
do {
if (!var6.hasNext()) {
return null;
}
pathPattern = (String)var6.next();
strings = pathPattern.split("==");
if (strings.length == 2) {
// 分割出url+httpMethod,判断httpMethod和request请求的method是否一致,不一致直接false
if (WebUtils.toHttp(request).getMethod().toUpperCase().equals(strings[1].toUpperCase())) {
flag = false;
} else {
flag = true;
}
} else {
flag = false;
}
pathPattern = strings[0];
} while(!this.pathMatches(pathPattern, requestURI) || flag);
if (log.isTraceEnabled()) {
log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "]. Utilizing corresponding filter chain...");
}
if (strings.length == 2) {
pathPattern = pathPattern.concat("==").concat(WebUtils.toHttp(request).getMethod().toUpperCase());
}
System.out.println("pathparttern:---"+pathPattern);
return filterChainManager.proxy(originalChain, pathPattern);
}
}
}
重写了ShiroFilterFactoryBean的createInstance方法:
public class RestShiroFilterFactoryBean extends ShiroFilterFactoryBean {
private static final Logger LOGGER = LoggerFactory.getLogger(RestShiroFilterFactoryBean.class);
public RestShiroFilterFactoryBean() {
super();
}
@Override
protected AbstractShiroFilter createInstance() throws Exception {
LOGGER.debug("Creating Shiro Filter instance.");
SecurityManager securityManager = this.getSecurityManager();
String msg;
if (securityManager == null) {
msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
} else if (!(securityManager instanceof WebSecurityManager)) {
msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
} else {
FilterChainManager manager = this.createFilterChainManager();
RestPathMatchingFilterChainResolver chainResolver = new RestPathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new RestShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver);
}
}
private static final class SpringShiroFilter extends AbstractShiroFilter {
protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
if (webSecurityManager == null) {
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
} else {
this.setSecurityManager(webSecurityManager);
if (resolver != null) {
this.setFilterChainResolver(resolver);
}
}
}
}
}
然后更改shiro配置文件使用自定义的RestShiroFilterFactoryBean:
@Bean
public RestShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
RestShiroFilterFactoryBean shiroFilterFactoryBean = new RestShiroFilterFactoryBean();
// 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager);
// setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
shiroFilterFactoryBean.setLoginUrl("/notLogin");
// 设置无权限时跳转的 url;
shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
// 设置拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/logout","anon");
filterChainDefinitionMap.put("/login","anon");
filterChainDefinitionMap.put("/user/**","roles[user]");
filterChainDefinitionMap.put("/test/**==POST","anon");
//其余接口一律拦截
//主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
System.out.println("Shiro拦截器工厂类注入成功");
return shiroFilterFactoryBean;
}
这里加了 filterChainDefinitionMap.put("/test/ * *==POST","anon");即只有post请求方式的/test/ ** 才不用验证,其余的都会匹配到/**进行authc身份认证