问题描述
在前后端分离整合shiro框架时,我发现shiro中的Subject主体只能在第一次访问时候拿到,甚至AuthenticationInfo认证完之后AuthorizationInfo授权不能通过
原因分析:
跨域时会先发送一个OPTIONS(预请求),这个预请求是不带AuthorizationInfo的,所以不能授权
解决方案:
public class CORSAuthenticationFilter extends FormAuthenticationFilter {
private static final Logger logger = LoggerFactory.getLogger(CORSAuthenticationFilter.class);
public CORSAuthenticationFilter() {
super();
}
@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
//Always return true if the request's method is OPTIONSif (request instanceof HttpServletRequest) {
if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
return true;
}
return super.isAccessAllowed(request, response, mappedValue);
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse res = (HttpServletResponse)response;
res.setHeader("Access-Control-Allow-Origin", "*");
res.setStatus(HttpServletResponse.SC_OK);
res.setCharacterEncoding("UTF-8");
PrintWriter writer = res.getWriter();
Map<String, Object> map= new HashMap<>();
map.put("code", 702);
map.put("msg", "未登录");
// writer.write(JSON.toJSONString(map));
writer.close();
return false;
}
}
自定义Filter,此步一定不要漏了
在shiro的配置类中一定要自定义Filter,免得会使用默认的Filter
@Configuration
public class ShiroConfig {
//1.创建shiroFilter //负责拦截所有请求
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//给filter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//自定义Filter
Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
filters.put("corsAuthenticationFilter",corsAuthenticationFilter());
shiroFilterFactoryBean.setFilters(filters);
//配置系统受限资源
//配置系统公共资源
Map<String,String> map = new HashMap<String,String>();
map.put("/login","anon");//anon 公共的资源路径,不需要认证,如果不设置登陆时候提交表单的url路径,则会死循环一直登录不上
map.put("/logout","anon");
map.put("/**","corsAuthenticationFilter");//corsAuthenticationFilter 请求这个资源需要认证和授权
//默认认证界面路径---当认证不通过时跳转
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
public CORSAuthenticationFilter corsAuthenticationFilter(){
return new CORSAuthenticationFilter();
}
//2.创建安全管理器
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("getRealm") Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 给安全管理器设置自定义MD5realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
//3.创建自定义realm
@Bean
public Realm getRealm(RedisTemplate redisTemplate){
CustomerRealm customerRealm = new CustomerRealm();
//获取hash凭证匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//设置加密算法为md5
credentialsMatcher.setHashAlgorithmName("MD5");
// 设置散列次数
credentialsMatcher.setHashIterations(1024);
// 修改凭证校验匹配器
customerRealm.setCredentialsMatcher(credentialsMatcher);
//开启缓存管理
customerRealm.setCacheManager(new RedisCacheManager());
//开启全局缓存
customerRealm.setCachingEnabled(true);
//开启认证缓存
customerRealm.setAuthenticationCachingEnabled(true);
customerRealm.setAuthenticationCacheName("authenticationCache");//设置缓存的字段名
//开启授权缓存
customerRealm.setAuthorizationCachingEnabled(true);
customerRealm.setAuthorizationCacheName("authorizationCache");//设置缓存的字段名
return customerRealm;
}
}
此步千万别忘