Spring Security 5.5.7升级后登录认证成功却遇会话超时/权限拒绝
升级Spring Security 5.5.7后认证成功却触发权限拒绝的问题解决
问题背景
将Spring及Spring Security升级至5.5.7版本后,输入有效凭据完成认证后,系统提示会话超时并重定向至“您无权访问此页面”的权限拒绝页面,升级前功能正常。
相关代码片段
login.jsp
<form role="form" id="loginForm" action="<c:url value="/j_spring_security_check" ></c:url>" method="post">
spring-security.xml
<security:http auto-config="true" use-expressions="true" create-session="ifRequired" authentication-manager-ref="authenticationManager" > <security:form-login login-page="/login-fail?error" default-target-url="/home" authentication-failure-url="/login-fail?error" username-parameter="username" password-parameter="password" login-processing-url="/j_spring_security_check" /> <security:logout invalidate-session="true" logout-success-url="/logout?logout" logout-url="/j_spring_security_logout" delete-cookies="JSESSIONID" /> <security:session-management invalid-session-url="/login-fail?error-timeout" session-fixation-protection="migrateSession"> <security:concurrency-control max-sessions="50" error-if-maximum-exceeded="false" expired-url="/login-fail?error-timeout" /> </security:session-management> <security:remember-me token-validity-seconds="3600" remember-me-parameter="remember" data-source-ref="dataSource" /> <security:headers> <security:frame-options policy="SAMEORIGIN" /> </security:headers> </security:http>
Home Controller核心代码
@SuppressWarnings("deprecation") @PreAuthorize("hasAnyRole('HOME')") @RequestMapping(value = { ControllerConstants.HOME,ControllerConstants.SSOHOME }, method = RequestMethod.GET) public ModelAndView home(@RequestParam(value = "isRsaSuccess", required=false) String isRsaSuccess,HttpServletRequest request) throws StaffUniqueContraintException,SMException{ ModelAndView model = new ModelAndView(); if(isRsaSuccess==null) isRsaSuccess="no"; User springUserBeanHome = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); // 剩余业务逻辑... }
错误日志
[ DEBUG ][ 27 Sep 2022 11:57:20,302 ] [ AspectControllerLogging ] : Arguments passed: [org.springframework.security.access.AccessDeniedException: Access is denied, SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@18f8cbc0], org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterResponse@7e4fc76a, en] [ ERROR ][ 27 Sep 2022 11:57:20,307 ] [ ExceptionHandlingController ] : Access is denied org.springframework.security.access.AccessDeniedException: Access is denied at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73) ~[spring-security-core-5.5.7.jar:5.5.7] at org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:238) ~[spring-security-core-5.5.7.jar:5.5.7] at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208) ~[spring-security-core-5.5.7.jar:5.5.7] at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:58) ~[spring-security-core-5.5.7.jar:5.5.7] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar:5.3.20] at com.elitecore.sm.iam.controller.HomeController$$EnhancerBySpringCGLIB$$a8dbedab.home(<generated>) ~[classes/:?] at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?] at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?] at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?] at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?] at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.20.jar:5.3.20] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.20.jar:5.3.20]
问题根源
Spring Security 5.x版本中,hasAnyRole()表达式会自动为传入的角色名添加ROLE_前缀进行匹配。如果你的用户权限列表中仅存在HOME(而非ROLE_HOME),就会导致权限校验失败,触发AccessDeniedException。
旧版本可能存在配置差异,或者权限加载逻辑未适配5.x的这一默认规则。
验证步骤
在HomeController的home方法开头添加日志,打印当前用户的权限集合,确认权限格式:
Authentication auth = SecurityContextHolder.getContext().getAuthentication(); System.out.println("当前用户权限列表: " + auth.getAuthorities());
若输出为[HOME]而非[ROLE_HOME],即可确认是前缀匹配问题。
解决方案
方案1:替换表达式为hasAnyAuthority()
hasAnyAuthority()不会自动添加前缀,直接匹配原始权限字符串,修改注解:
@PreAuthorize("hasAnyAuthority('HOME')")
方案2:调整权限加载逻辑,添加ROLE_前缀
在用户认证流程中,为角色名统一添加ROLE_前缀。例如在UserDetailsService实现类中:
List<GrantedAuthority> authorities = new ArrayList<>(); // roles为从数据源获取的角色列表,如["HOME"] for (String role : roles) { authorities.add(new SimpleGrantedAuthority("ROLE_" + role)); }
方案3:关闭默认角色前缀(不推荐)
若不想修改业务代码,可通过配置关闭自动添加前缀的行为,但会改变Spring Security默认规则,可能影响其他权限校验:
<security:global-method-security pre-post-annotations="enabled"> <security:expression-handler ref="methodSecurityExpressionHandler"/> </security:global-method-security> <bean id="methodSecurityExpressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"> <property name="defaultRolePrefix" value=""/> </bean>
额外排查点
- 确认
session-fixation-protection="migrateSession"配置在升级后正常工作,认证后会话中的用户权限信息是否正确迁移。 - 临时关闭
remember-me功能,排查是否是记住我逻辑干扰了权限加载。
内容的提问来源于stack exchange,提问作者Balaji Kathe
相关产品推荐
相关产品推荐

