springboot集成shiro遭遇自定义filter异常

2024-06-14 11:08

本文主要是介绍springboot集成shiro遭遇自定义filter异常,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先简述springboot使用maven集成shiro
1、用maven添加shiro

    <!--shiro--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>1.4.0</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version></dependency>

2、配置shiro

import com.yuntu.intelligent.log.service.QueryPermissionService;
import com.yuntu.intelligent.log.service.shiro.authc.AccountSubjectFactory;
import com.yuntu.intelligent.log.service.shiro.filter.AuthenticatedFilter;
import com.yuntu.intelligent.log.service.shiro.filter.QueryLimitFiter;
import com.yuntu.intelligent.log.service.shiro.realm.AccountRealm;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.codec.Base64;
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.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;/*** shiro权限管理的配置*/
@Configuration
public class ShiroConfig {@Beanpublic AccountSubjectFactory accountSubjectFactory() {return new AccountSubjectFactory();}/*** 安全管理器*/@Beanpublic DefaultWebSecurityManager securityManager(CookieRememberMeManager rememberMeManager, CacheManager cacheShiroManager, SessionManager sessionManager) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(this.shiroAccountRealm());securityManager.setCacheManager(cacheShiroManager);securityManager.setRememberMeManager(rememberMeManager);securityManager.setSessionManager(sessionManager);securityManager.setSubjectFactory(this.accountSubjectFactory());return securityManager;}/*** session管理器(单机环境)*/@Beanpublic DefaultWebSessionManager defaultWebSessionManager(CacheManager cacheShiroManager) {DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setCacheManager(cacheShiroManager);sessionManager.setSessionValidationInterval(1800 * 1000);sessionManager.setGlobalSessionTimeout(900 * 1000);sessionManager.setDeleteInvalidSessions(true);sessionManager.setSessionValidationSchedulerEnabled(true);Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);cookie.setName("shiroCookie");cookie.setHttpOnly(true);sessionManager.setSessionIdCookie(cookie);return sessionManager;}/*** 缓存管理器 使用Ehcache实现*/@Beanpublic CacheManager getCacheShiroManager() {return new MemoryConstrainedCacheManager();}/*** 项目自定义的Realm*/@Beanpublic AccountRealm shiroAccountRealm() {return new AccountRealm();}/*** rememberMe管理器, cipherKey生成见{@code Base64Test.java}*/@Beanpublic CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) {CookieRememberMeManager manager = new CookieRememberMeManager();manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));manager.setCookie(rememberMeCookie);return manager;}/*** 记住密码Cookie*/@Beanpublic SimpleCookie rememberMeCookie() {SimpleCookie simpleCookie = new SimpleCookie("rememberMe");simpleCookie.setHttpOnly(true);simpleCookie.setMaxAge(7 * 24 * 60 * 60);//7天return simpleCookie;}/*** Shiro的过滤器链*/@Beanpublic ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager,CollectionPropertiesConfig collectionPropertiesConfig,QueryPermissionService queryPermissionService) {ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);/*** 默认的登陆访问url*/shiroFilter.setLoginUrl("/login");/*** 登陆成功后跳转的url*/shiroFilter.setSuccessUrl("/");/*** 没有权限跳转的url*/shiroFilter.setUnauthorizedUrl("/error/reject.html");/*** 覆盖默认的user拦截器(默认拦截器解决不了ajax请求 session超时的问题,若有更好的办法请及时反馈作者)*/HashMap<String, Filter> myFilters = new HashMap<>();myFilters.put("query", new QueryLimitFiter(queryPermissionService));myFilters.put("authc", new AuthenticatedFilter());shiroFilter.setFilters(myFilters);/*** 配置shiro拦截器链** anon  不需要认证* authc 需要认证* user  验证通过或RememberMe登录的都可以** 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的** 顺序从上到下,优先级依次降低**/Map<String, String> hashMap = new LinkedHashMap<>();hashMap.put("/login", "anon");hashMap.put("/", "authc");hashMap.put("/user*", "authc");hashMap.put("/user/**", "authc");hashMap.put("/post/**", "authc");hashMap.put("/admin", "authc,perms[admin]");hashMap.put("/admin/**", "authc,perms[admin]");for(String uri:collectionPropertiesConfig.getQueryLogUrls()){hashMap.put(uri,"query");}shiroFilter.setFilterChainDefinitionMap(hashMap);return shiroFilter;}/*** 在方法中 注入 securityManager,进行代理控制*/@Beanpublic MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) {MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");bean.setArguments(new Object[]{securityManager});return bean;}/*** Shiro生命周期处理器:* 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm)* 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager)*/@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 启用shrio授权注解拦截方式,AOP式方法级权限检查*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}}

3、实现自定义的Realm、filter、SubjectFactory等:

import com.yuntu.intelligent.log.model.sysmodel.OrganizationUser;
import org.apache.shiro.authc.SimpleAuthenticationInfo;public class AccountAuthenticationInfo extends SimpleAuthenticationInfo{private static final long serialVersionUID = 3405356595200877071L;private OrganizationUser profile;public AccountAuthenticationInfo(){}public AccountAuthenticationInfo(Object principal, Object credentials, String realmName){super(principal, credentials, realmName);}public OrganizationUser getProfile() {return profile;}public void setProfile(OrganizationUser profile) {this.profile = profile;}
}import com.yuntu.intelligent.log.model.sysmodel.OrganizationUser;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.web.subject.support.WebDelegatingSubject;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;public class AccountSubject extends WebDelegatingSubject{private OrganizationUser profile;public AccountSubject(PrincipalCollection principals, boolean authenticated, String host, Session session,boolean sessionEnabled, ServletRequest request, ServletResponse response, SecurityManager securityManager, OrganizationUser profile) {super(principals, authenticated, host, session, sessionEnabled, request, response, securityManager);this.profile = profile;}public String getUsername(){return getPrincipal().toString();}public OrganizationUser getProfile() {return profile;}}import com.yuntu.intelligent.log.model.sysmodel.OrganizationUser;
import com.yuntu.intelligent.log.service.UserService;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.SubjectContext;
import org.apache.shiro.web.subject.WebSubjectContext;
import org.springframework.beans.factory.annotation.Autowired;public class AccountSubjectFactory implements SubjectFactory {@Autowiredprivate UserService userService;@Overridepublic Subject createSubject(SubjectContext context) {WebSubjectContext wsc = (WebSubjectContext) context;AuthenticationInfo info = wsc.getAuthenticationInfo();OrganizationUser profile = null;AccountSubject subject = null;if (info instanceof AccountAuthenticationInfo) {profile = ((AccountAuthenticationInfo) info).getProfile();subject = doCreate(wsc, profile);subject.getSession(true).setAttribute("profile", profile);}else{Session session = wsc.getSession();if(session != null){profile = (OrganizationUser)session.getAttribute("profile");}subject = doCreate(wsc, profile);boolean isRemembered = subject.isRemembered();if (session == null) {wsc.setSessionCreationEnabled(true);subject.getSession(true);}if (isRemembered && profile == null) {Object username = subject.getPrincipal();profile = userService.getUserByName((String) username);subject.getSession(true).setTimeout(30 * 60 * 1000);subject.getSession(true).setAttribute("profile", profile);}}return doCreate(wsc, profile);}private AccountSubject doCreate(WebSubjectContext wsc, OrganizationUser profile) {return new AccountSubject(wsc.resolvePrincipals(), wsc.resolveAuthenticated(), wsc.resolveHost(),wsc.resolveSession(), wsc.isSessionCreationEnabled(), wsc.resolveServletRequest(),wsc.resolveServletResponse(), wsc.resolveSecurityManager(), profile);}
}import org.apache.shiro.authc.AuthenticationToken;public class AccountToken implements AuthenticationToken {private static final long serialVersionUID = 1L;private long id;private String username;public AccountToken() {}public AccountToken(long id, final String username) {this(id, username, username);}public AccountToken(long id, final String username, String nickname) {this.id = id;this.username = username;}@Overridepublic Object getPrincipal() {return username;}@Overridepublic Object getCredentials() {return null;}public long getId() {return id;}public void setId(long id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}}import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.servlet.OncePerRequestFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Formatter;/*** @version 1.0.0*/
public class AuthenticatedFilter extends OncePerRequestFilter {private Logger LOG = LoggerFactory.getLogger(AuthenticatedFilter.class);private static final String JS = "<script type='text/javascript'>var wp=window.parent; if(wp!=null){while(wp.parent&&wp.parent!==wp){wp=wp.parent;}wp.location.href='%1$s';}else{window.location.href='%1$s';}</script>";private String loginUrl = "/login";@Overrideprotected void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)throws ServletException, IOException {LOG.info("开始权限验证");Subject subject = SecurityUtils.getSubject();if (subject.isAuthenticated()) {chain.doFilter(request, response);} else {identifyGuest(subject, request, response, chain);}}protected void identifyGuest(Subject subject, ServletRequest request, ServletResponse response, FilterChain chain)throws ServletException, IOException {redirectLogin(request, response);}protected void redirectLogin(ServletRequest request, ServletResponse response) throws IOException {WebUtils.saveRequest(request);String path = WebUtils.getContextPath((HttpServletRequest) request);String url = loginUrl;if (StringUtils.isNotBlank(path) && path.length() > 1) {url = path + url;}if (isAjaxRequest((HttpServletRequest) request)) {response.setContentType("application/json;charset=UTF-8");response.getWriter().print("您还没有登录!");} else {response.getWriter().write(new Formatter().format(JS, url).toString());}}public String getLoginUrl() {return loginUrl;}public void setLoginUrl(String loginUrl) {this.loginUrl = loginUrl;}/*** 判断是否为Ajax请求 <功能详细描述>* * @param request* @return 是true, 否false* @see [类、类#方法、类#成员]*/public static boolean isAjaxRequest(HttpServletRequest request) {String header = request.getHeader("X-Requested-With");if (header != null && "XMLHttpRequest".equals(header))return true;elsereturn false;}}import com.yuntu.intelligent.log.service.QueryPermissionService;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.web.servlet.AbstractFilter;
import org.apache.shiro.web.servlet.ServletContextSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;public class QueryLimitFiter extends ServletContextSupport implements Filter {private QueryPermissionService queryPermissionService;private static final transient Logger log = LoggerFactory.getLogger(AbstractFilter.class);protected FilterConfig filterConfig;public QueryLimitFiter(QueryPermissionService queryPermissionService) {this.queryPermissionService = queryPermissionService;}public FilterConfig getFilterConfig() {return this.filterConfig;}public void setFilterConfig(FilterConfig filterConfig) {this.filterConfig = filterConfig;this.setServletContext(filterConfig.getServletContext());}protected String getInitParam(String paramName) {FilterConfig config = this.getFilterConfig();return config != null? org.apache.shiro.util.StringUtils.clean(config.getInitParameter(paramName)):null;}public final void init(FilterConfig filterConfig) throws ServletException {this.setFilterConfig(filterConfig);try {this.onFilterConfigSet();} catch (Exception var3) {if(var3 instanceof ServletException) {throw (ServletException)var3;} else {if(log.isErrorEnabled()) {log.error("Unable to start Filter: [" + var3.getMessage() + "].", var3);}throw new ServletException(var3);}}}protected void onFilterConfigSet() throws Exception {}public void destroy() {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {if(isAccessAllowed(servletRequest,servletResponse)){filterChain.doFilter(servletRequest,servletResponse);}else {servletResponse.setContentType("application/json;charset=UTF-8");servletResponse.getWriter().print("不允许查询!");}}protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse) {HttpServletRequest request = (HttpServletRequest)servletRequest;String hallCode = request.getParameter("guanhao");String uri = request.getRequestURI();System.out.println(uri);
//        if (collectionPropertiesConfig.getQueryLogUrls().contains(uri)) {try {if (StringUtils.isEmpty(hallCode)) {servletResponse.setContentType("application/json;charset=UTF-8");servletResponse.getWriter().print("需要输入馆号!");return false;}if (queryPermissionService.permit(hallCode)) {return true;} else {servletResponse.setContentType("application/json;charset=UTF-8");servletResponse.getWriter().print("你没有权限查询此馆!");return false;}}catch (Exception e){e.printStackTrace();}return false;}
}import com.yuntu.intelligent.log.model.sysmodel.OrganizationPrivilege;
import com.yuntu.intelligent.log.model.sysmodel.OrganizationResources;
import com.yuntu.intelligent.log.model.sysmodel.OrganizationUser;
import com.yuntu.intelligent.log.service.RoleService;
import com.yuntu.intelligent.log.service.UserService;
import com.yuntu.intelligent.log.service.shiro.authc.AccountAuthenticationInfo;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
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.springframework.beans.factory.annotation.Autowired;import javax.annotation.Resource;
import java.util.List;public class AccountRealm extends AuthorizingRealm {@Autowiredprivate UserService userService;@Autowiredprivate RoleService userRoleService;public AccountRealm() {super(new AllowAllCredentialsMatcher());setAuthenticationTokenClass(UsernamePasswordToken.class);}@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {String username = (String) principals.fromRealm(getName()).iterator().next();if (username != null) {OrganizationUser user = userService.getUserByName(username);if (user != null) {SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();OrganizationPrivilege role = userRoleService.getRole(user.getPrivilegeId());List<OrganizationResources> roleResources = userRoleService.getRoleResources(user.getPrivilegeId());//赋予角色info.addRole(role.getName());//赋予权限roleResources.forEach(resource -> info.addStringPermission(resource.getPermission()));return info;}}return null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {OrganizationUser profile = getAccount(userService, token);if (profile.getStatus() == 0) {throw new LockedAccountException(profile.getUserId());}AccountAuthenticationInfo info = new AccountAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());info.setProfile(profile);return info;}protected OrganizationUser getAccount(UserService userService, AuthenticationToken token) {UsernamePasswordToken upToken = (UsernamePasswordToken) token;return userService.login(upToken.getUsername(), String.valueOf(upToken.getPassword()));}
}

4、重点记录filter配置中出现的问题。
如上代码,我有2个自定义的过滤器,AuthenticatedFilter可以抛开spring运行,而QueryLimitFiter想使用一些spring管理的bean,所以一开始QueryLimitFiter是使用@Component注释并且在ShiroConfig的@Bean方法中将其注入并配置进shiro的过滤器链。
在使用的时候,原定只是在个别uri触发的QueryLimitFiter,所有uri都会触发它,反而AuthenticatedFilter失效了。查找原因,找到spring的filter链如图,这是借用别人的图,我的图是将accessTokenFilter替换为queryLimitFiter:
这里写图片描述
总之就是自定义的过滤器QueryLimitFiter居然在shiroFilter之外而且运行在shiroFilter之前了。。。

5、解决方案,如上代码,将QueryLimitFiter不交给spring托管,使用new的方式添加到shiro的过滤器链中就没有问题了。出现原因可能是spring会自动将我们自定义的filter加载到它的过滤器链中(待深究!)。

这篇关于springboot集成shiro遭遇自定义filter异常的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1060228

相关文章

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

springboot项目中整合高德地图的实践

《springboot项目中整合高德地图的实践》:本文主要介绍springboot项目中整合高德地图的实践,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一:高德开放平台的使用二:创建数据库(我是用的是mysql)三:Springboot所需的依赖(根据你的需求再

spring中的ImportSelector接口示例详解

《spring中的ImportSelector接口示例详解》Spring的ImportSelector接口用于动态选择配置类,实现条件化和模块化配置,关键方法selectImports根据注解信息返回... 目录一、核心作用二、关键方法三、扩展功能四、使用示例五、工作原理六、应用场景七、自定义实现Impor

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.