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 实用工具类Spring 的 AnnotationUtils详解

《Java实用工具类Spring的AnnotationUtils详解》Spring框架提供了一个强大的注解工具类org.springframework.core.annotation.Annot... 目录前言一、AnnotationUtils 的常用方法二、常见应用场景三、与 JDK 原生注解 API 的

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Java中的StringBuilder之如何高效构建字符串

《Java中的StringBuilder之如何高效构建字符串》本文将深入浅出地介绍StringBuilder的使用方法、性能优势以及相关字符串处理技术,结合代码示例帮助读者更好地理解和应用,希望对大家... 目录关键点什么是 StringBuilder?为什么需要 StringBuilder?如何使用 St

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll