遇到的一个问题:ShiroFilterFactoryBean 配置完成后,该filter不起作用。
原因:在项目中还定义了一个别的DotDOFilter(该Filter的作用是把Url中xxx.do结尾Url修改成xxx),
在这个DotDOFilter中使用了request.getRequestDispatcher(uri).forward(request, response);
在这个DotDOFilter之后的ShiroFilter就不起作用了。
注意:Filter可以指定DispatcherType的类型,详见https://blog.csdn.net/xiaokang123456kao/article/details/72885171
处理方式:把ShiroFilter放在Filter前面即可.
@Bean
public FilterRegistrationBean securityFilterChain(AbstractShiroFilter securityFilter) {
FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
registration.setOrder(Integer.MAX_VALUE-1);//排在DotDOFilter的前面
registration.setName(“shiroFilter”);
//registration.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.FORWARD);
registration.setDispatcherTypes(DispatcherType.REQUEST);
return registration;
}
查看Filter顺序的方法:
参考:
1)filter中的dispatcher标签解析 https://blog.csdn.net/xiaokang123456kao/article/details/72885171
2)Shiro和AJAX完美整合 https://blog.csdn.net/wu560130911/article/details/11819761
3)springboot(十四):springboot整合shiro-登录认证和权限管理 https://blog.csdn.net/ityouknow/article/details/73836159
4)Springboot与shiro整合遇到的坑 https://zhuanlan.zhihu.com/p/29161098
代码:
1)ShiroConfig.java
package com.alf.test.platform.config;
import com.alf.test.platform.shiro.*;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.mgt.SecurityManager;
import org.springframework.context.annotation.DependsOn;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
/**
* 配置Url过滤器
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//获取filters
filters.put("authc", new CustomFormAuthenticationFilter());//注入自定义的FormAuthenticationFilter
filters.put("logout", new CustomLogoutFilter());//注入自定义的LogoutFilter
shiroFilterFactoryBean.setSecurityManager(securityManager);
//拦截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
// 配置不会被拦截的链接 顺序判断
//filterChainDefinitionMap.put("/abctest/test1", "myFilter1");
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/swagger-resources", "anon");
filterChainDefinitionMap.put("/v2/api-docs", "anon");
filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon");
// filterChainDefinitionMap.put("/login", "anon");
// filterChainDefinitionMap.put("/logout", "anon");
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/logout.do", "logout");
//<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
// filterChainDefinitionMap.put("/**", "myFilter1,authc");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
//shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
// Map<String, Filter> map = new HashMap<>();
// map.put("myFilter1", new MyOncePerRequestFilter());
// shiroFilterFactoryBean.setFilters(map);
return shiroFilterFactoryBean;
}
@Bean
public FilterRegistrationBean securityFilterChain(AbstractShiroFilter securityFilter) {
FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
registration.setOrder(Integer.MAX_VALUE-1);//排在ZHandleDotDOFilter的前面
registration.setName("shiroFilter");
//registration.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.FORWARD);
registration.setDispatcherTypes(DispatcherType.REQUEST);
return registration;
}
/**
* Shiro生命周期处理器
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
* @return
*/
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
/**
* 注入Realm
* @return
*/
@Bean
public QdasShiroRealm qdasShiroRealm(){
QdasShiroRealm qdasShiroRealm = new QdasShiroRealm();
return qdasShiroRealm;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(qdasShiroRealm());
securityManager.setSessionManager(configWebSessionManager());
//securityManager.setCacheManager();
return securityManager;
}
@Bean
public SessionManager configWebSessionManager(){
DefaultWebSessionManager manager = new DefaultWebSessionManager();
//manager.setCacheManager(cacheManager);// 加入缓存管理器
//manager.setSessionDAO(sessionDao);// 设置SessionDao
manager.setDeleteInvalidSessions(true);// 删除过期的session
manager.setGlobalSessionTimeout(1800000);// 设置全局session超时时间
manager.setSessionValidationSchedulerEnabled(true);// 是否定时检查session
manager.setSessionIdCookie(simpleCookie());
return manager;
}
/**
* 注入cookie模板
* @return
*/
@Bean
public SimpleCookie simpleCookie(){
SimpleCookie simpleCookie = new SimpleCookie("sid-shrio");
simpleCookie.setMaxAge(-1);
simpleCookie.setHttpOnly(true);
return simpleCookie;
}
}
2)CustomFormAuthenticationFilter.java
package com.alf.test.platform.shiro;
import com.alibaba.fastjson.JSONObject;
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.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
/**
* 自定义FormAuthenticationFilter:
* 1 修改用户名和密码的获取方式,使用sid
* 2 定义了登陆的Url
* 3 修改了登陆成功和失败的处理方式
*/
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
private static final Logger logger = LoggerFactory.getLogger(CustomFormAuthenticationFilter.class);
public CustomFormAuthenticationFilter(){
super();
}
@Override
protected String getUsername(ServletRequest request) {
try {
JSONObject jsonObject = getJsonObject(request);
return jsonObject.getString("sid");
} catch (IOException e) {
e.printStackTrace();
}
return super.getUsername(request);
}
public static JSONObject getJsonObject(ServletRequest request) throws IOException{
String resultStr = "";
String readLine;
StringBuffer sb = new StringBuffer();
BufferedReader responseReader = null;
OutputStream outputStream = null;
try {
responseReader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
while ((readLine = responseReader.readLine()) != null) {
sb.append(readLine).append("
");
}
//responseReader.close();
resultStr = sb.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
//outputStream.close();
}
}
return JSONObject.parseObject(resultStr);
}
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
// 在这里进行验证码的校验
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpSession session = httpServletRequest.getSession();
return super.onAccessDenied(request, response);
}
/**
* 目前项目中登陆URL为:/login.do, /login
* @param request
* @param response
* @return
*/
@Override
protected boolean isLoginRequest(ServletRequest request, ServletResponse response) {
String requestURI = getPathWithinApplication(request);
if ("/login.do".equals(requestURI) || "/login".equals(requestURI)){
return true;
}
return false;
}
/**
* 目前使用XXXsid登陆, 不需要密码
* @param request
* @return
*/
@Override
protected String getPassword(ServletRequest request) {
return "";
}
/**
* 登陆成功, 直接执行接下来的流程
* @param token
* @param subject
* @param request
* @param response
* @return
* @throws Exception
*/
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
ServletRequest request, ServletResponse response) throws Exception {
logger.info("登陆成功!");
return true;
}
/**
* 重写该方法, 使用Dispatcher, 转发到指定controller
* @param request
* @param response
* @throws IOException
*/
protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
try {
request.getRequestDispatcher("/noLogin").forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
}
}
}
3)CustomLogoutFilter.java
package com.alf.test.platform.shiro;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Locale;
public class CustomLogoutFilter extends LogoutFilter {
private static final Logger logger = LoggerFactory.getLogger(CustomLogoutFilter.class);
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
Subject subject = getSubject(request, response);
// Check if POST only logout is enabled
if (isPostOnlyLogout()) {
// check if the current request's method is a POST, if not redirect
if (!WebUtils.toHttp(request).getMethod().toUpperCase(Locale.ENGLISH).equals("POST")) {
return onLogoutRequestNotAPost(request, response);
}
}
//String redirectUrl = getRedirectUrl(request, response, subject);
//try/catch added for SHIRO-298:
// try {
// subject.logout();
// } catch (SessionException ise) {
// logger.debug("Encountered session exception during logout. This can generally safely be ignored.", ise);
// }
//issueRedirect(request, response, redirectUrl);
return true;
}
@Override
public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
Subject subject = getSubject(request, response);
try {
subject.logout();
} catch (SessionException ise) {
logger.debug("Encountered session exception during logout. This can generally safely be ignored.", ise);
}
}
}
4)DotDOFilter.java
package com.alf.test.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.RequestContextFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 处理aaa.do访问aaa的问题
*/
@Configuration
@WebFilter(urlPatterns = "/")
public class DotDOFilter extends RequestContextFilter {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String uri = request.getRequestURI();
if (uri.endsWith(".do")) {
uri = uri.substring(0, uri.length() - 3);
request.getRequestDispatcher(uri).forward(request, response);
} else {
super.doFilterInternal(request, response, filterChain);
}
}
}