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

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Debezium 与 Apache Kafka 的集成方式步骤详解

《Debezium与ApacheKafka的集成方式步骤详解》本文详细介绍了如何将Debezium与ApacheKafka集成,包括集成概述、步骤、注意事项等,通过KafkaConnect,D... 目录一、集成概述二、集成步骤1. 准备 Kafka 环境2. 配置 Kafka Connect3. 安装 D

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.