spring security异常记录:org.springframework.security.access.AccessDeniedException: Access is denied

Posted sword to coding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring security异常记录:org.springframework.security.access.AccessDeniedException: Access is denied相关的知识,希望对你有一定的参考价值。

最近在做ssm项目的时候用到spring security时遇到了异常,如下所示:

15:06:28,144 DEBUG FilterSecurityInterceptor:348 - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@52dd6d71: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
15:06:28,180 DEBUG AffirmativeBased:66 - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@1f4acb36, returned: -1
15:06:28,185 DEBUG ExceptionTranslationFilter:181 - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
	at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84)
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

网上查阅了很多资料,出现这种异常主要有以下几种原因:

情况一

用户已经登录成功,但是该用户的权限不能够操作受限制的页面
我看了我的配置文件,发现出现的错误并不是该原因:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/mvc
						http://www.springframework.org/schema/mvc/spring-mvc.xsd
						http://code.alibabatech.com/schema/dubbo
						http://code.alibabatech.com/schema/dubbo/dubbo.xsd
						http://www.springframework.org/schema/context
						http://www.springframework.org/schema/context/spring-context.xsd
                     http://www.springframework.org/schema/security
                     http://www.springframework.org/schema/security/spring-security.xsd">

<!--    定义放行的页面-->
    <security:http security="none" pattern="/login.html"></security:http>
    <security:http security="none" pattern="/css/**"></security:http>
    <security:http security="none" pattern="/img/**"></security:http>
    <security:http security="none" pattern="/js/**"></security:http>
    <security:http security="none" pattern="/plugins/**"></security:http>

<!--    定义保护的页面,只要认证通过就放行,具体的权限使用注解的方式去拦截-->
    <security:http auto-config="true" use-expressions="true">
        <security:intercept-url pattern="/pages/**" access="isAuthenticated()"></security:intercept-url>

        <security:headers>
            <!--设置在页面可以通过iframe访问受保护的页面,默认为不允许访问-->
            <security:frame-options policy="SAMEORIGIN"></security:frame-options>
        </security:headers>

<!--        自定义登陆页面-->
        <security:form-login
            login-page="/login.html"
            username-parameter="username"
            password-parameter="password"
            login-processing-url="/login.do"
            default-target-url="/pages/main.html"
            authentication-failure-url="/login.html"
        ></security:form-login>
        <security:csrf disabled="true"></security:csrf>


<!--        退出登录-->
        <security:logout
                logout-url="/logout.do"
                logout-success-url="/login.html"
                invalidate-session="true"></security:logout>
    </security:http>

<!--    授权关系管理-->
    <security:authentication-manager>
        <security:authentication-provider user-service-ref="springSecurityUserService">
            <security:password-encoder ref="passwordEncoder"></security:password-encoder>
        </security:authentication-provider>
    </security:authentication-manager>

    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>

    <security:global-method-security pre-post-annotations="enabled"></security:global-method-security>

</beans>

其中

<security:intercept-url pattern="/pages/**" access="isAuthenticated()"></security:intercept-url>

表示只要是登录成功的都可以访问pages下的页面,但是我登录了却还是报错

情况二:

网上的观点:

情况三:

我的分析:

org.springframework.security.access.AccessDeniedException: Access is denied
	at 

这个异常在spring security中通常都是指登录的用户没有权限访问请求的资源。但是我看了配置文件是正常配置了访问权限的。之后我又看了打印的日志信息:

我的异常出现之前都会有这个日志,我的猜想是无论我登录了怎样的用户,被授予的权限都是ROLE_ANONYMOUS (表示匿名用户)
所以导致我无权访问被保护的页面。

解决方案

已解决

我的错误:禁用了网站的cookie

允许cookie即可

参考文章:
Spring Security出现问题:Access is denied 及我的解决方案

User Granted Authorities are always : ROLE_ANONYMOUS?

登录不跳转RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS

甚至没有调用自定义 Spring Security 身份验证提供程序?

【中文标题】甚至没有调用自定义 Spring Security 身份验证提供程序?【英文标题】:Custom Spring Security Authentication provider not even being called? 【发布时间】:2021-12-25 19:04:32 【问题描述】:

我需要从 3rd 方应用授权用户。基本上这是一个 REST api 调用,但这不是问题。每次我导航到一个页面时,我都会自动重定向到错误页面,根本没有任何解释。日志中没有任何内容,即使我的日志记录:logging.level.org.springframework.security=DEBUG 和我的根级别为 WARN

我的安全配置如下所示:

@Configuration
@EnableWebSecurity
@ConfigurationProperties("security")
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

  @Autowired
  ELPAuthenticationProvider authenticationProvider;

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception 
    auth.authenticationProvider(authenticationProvider);
  

  @Override
  protected void configure(HttpSecurity http) throws Exception 
    elpLogger.debug("****************Configuring HttpSecurity");
    http.authorizeRequests().antMatchers("/hello/**").permitAll();
    http.authorizeRequests().anyRequest().authenticated();
  


还有我的身份验证提供者:

@Component
public class ELPAuthenticationProvider implements AuthenticationProvider 

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException 
    logger.debug("In Authenticate");
    final List<GrantedAuthority> grantedAuths = new ArrayList<>();
    grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
    final UserBean principal = new UserBean("admin", "password", grantedAuths);
    final Authentication auth = new UsernamePasswordAuthenticationToken(principal, "password", grantedAuths);
    return auth;
  

  @Override
  public boolean supports(Class<? extends Object> authentication) 
    return true;
  

对我来说,这看起来应该验证任何东西。但是除了我的 HelloWorldController ("/hello") 之外的所有东西我都会被扔到我的错误页面而没有任何解释。我的日志如下所示:

o.s.security.web.FilterChainProxy        : Securing GET /
s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
o.s.s.w.a.i.FilterSecurityInterceptor    : Failed to authorize filter invocation [GET /] with attributes [authenticated]
o.s.s.w.s.HttpSessionRequestCache        : Saved request http://localhost:7080/ to session
o.s.s.w.a.Http403ForbiddenEntryPoint     : Pre-authenticated entry point called. Rejecting access
w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
c.e.web.controllers.ELPErrorController   : ************************  Error handler

所以我不知道为什么我的身份验证提供程序甚至没有被调用,其次,我不知道为什么会抛出异常。 (或者为什么我被重定向到错误页面而不是未授权页面)

有什么想法吗?

编辑 我从 AuthenticationProvider 中删除了 @Component 注释,并在我的主 Application.java 中将其声明为 bean 将其自动连接到 SecurityConfiguration 中。我在上面的示例中进行了更改。完全相同的问题。没有变化。

【问题讨论】:

请 (1.) 还要确保 logging.level.&lt;package.of.elpauthenticationprovider&gt;=debug 和 (2.) 它是组件(自动扫描?)还是 bean? (我知道/理解它是完全一样的,但可能是冲突的......当一起使用时(没有警告??)) @xerx593 没有变化。日志级别是调试。但我确实有两个组件注释,并将其声明为 Bean。我去掉了组件注解,把Bean方法放到Application.java中我相应地调整了问题中的代码。 除了下面的@fast-reflexes 回答之外,请确保您不要两次致电http.authorizeRequests()。第二次调用会覆盖第一次。您可以将规则链接在一起,如:http.authorizeRequests().antMatchers("/hello/**").permitAll()..anyRequest().authenticated(). 【参考方案1】:

鉴于AuthenticationProvider 接口中的authenticate 方法将身份验证作为参数,我们可以放心,必须进行某种初始身份验证,以便为提供者提供一些工作。以下摘录在 Kotlin 中给出。

即使使用带有基本身份验证的标准设置

@Configuration
class SecurityAssets 

    @Bean
    fun passwordEncoder(): PasswordEncoder =
        return BCryptPasswordEncoder()

 

@EnableWebSecurity
class Config(val encoder: PasswordEncoder): WebSecurityConfigurerAdapter() 

    override fun configure(auth: AuthenticationManagerBuilder) 
        auth
            .inMemoryAuthentication()         
               .withUser("user")
               .password(encoder.encode("password"))
               .roles("USER")
    

    override fun configure(http: HttpSecurity) 
        http
            .authorizeRequests()
                .anyRequest()
                    .authenticated()
     

以上内容不会触发身份验证。为此,我们需要为 Spring 添加一些方法来创建初始身份验证,然后由配置的提供者进行身份验证:

@EnableWebSecurity
class Config(val encoder: PasswordEncoder): WebSecurityConfigurerAdapter() 

    ...

    override fun configure(http: HttpSecurity) 
        http
            .authorizeRequests()
                .anyRequest()
                    .authenticated()
                    .and()
                    .httpBasic()
     

现在可以了!

在你的情况下,类似的事情也会起作用:

class CustomAuthenticationProvider : AuthenticationProvider 

    override fun authenticate(authentication: Authentication): Authentication 
        println("In Authenticate")
        val grantedAuths: MutableList<GrantedAuthority> = ArrayList()
        grantedAuths.add(SimpleGrantedAuthority("ROLE_USER"))
        val principal = AuthenticatedPrincipal  "Name" 
        return UsernamePasswordAuthenticationToken(principal, "password", grantedAuths)
    

    override fun supports(authentication: Class<*>?): Boolean 
        println("In supports")
        return true
    


@EnableWebSecurity
class Config(val encoder: PasswordEncoder): WebSecurityConfigurerAdapter() 

    override fun configure(auth: AuthenticationManagerBuilder) 
        auth
            .authenticationProvider(CustomAuthenticationProvider())
    

    override fun configure(http: HttpSecurity) 
        http
            .authorizeRequests()
                .anyRequest()
                    .authenticated()
                    .and()
                    .httpBasic()
     

如何使自定义提供程序对配置器可用并不重要,您可以使用 bean 或简单地在需要时将其实例化。

如果您需要一些不同的主要身份验证方法,您可以将httpBasic 更改为其他内容。如果您需要自定义实际的身份验证方法(例如,查找一些自定义标头或其他过滤器尚不存在的东西),那么您应该实现一个自定义过滤器,该过滤器被添加到安全过滤器链中,然后委托给您的提供商。

安全过滤器链中的过滤器是实际处理请求的内容,简单地添加安全提供程序不会添加新过滤器,在本例中,httpBasic 会添加新过滤器。

【讨论】:

以上是关于spring security异常记录:org.springframework.security.access.AccessDeniedException: Access is denied的主要内容,如果未能解决你的问题,请参考以下文章

如何处理 grails spring-security-rest 插件中的自定义身份验证异常?

甚至没有调用自定义 Spring Security 身份验证提供程序?

Spring Security入门(3-4)Spring Security 异常处理异常传递和异常获取

Spring Security 不会抛出任何异常

Spring BootSpring Security登陆异常出路

Spring Security —— 异常处理