关于java:Spring Security:AnonymousAuthenticationToken vs UsernamePasswordAuthenticationToken

Spring Security: AnonymousAuthenticationToken vs UsernamePasswordAuthenticationToken

我正在研究Gradle / Java 1.8 / Spring Boot,Spring Integration,Spring Batch,Spring Data Rest项目(我继承了)。

这是来自以下版本的依赖项:build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Spring Boot
compile("org.springframework.boot:spring-boot-starter-ws")
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-integration")
compile("org.springframework.boot:spring-boot-starter-actuator")
compile("org.springframework.boot:spring-boot-starter-security")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-batch")

// Spring integration
compile("org.springframework.integration:spring-integration-core")
compile("org.springframework.integration:spring-integration-ws")
compile("org.springframework.integration:spring-integration-jpa")
compile("org.springframework.integration:spring-integration-sftp")

// Spring batch
compile("org.springframework.batch:spring-batch-core")
compile("org.springframework.batch:spring-batch-integration")

// Spring Data REST
compile("org.springframework.data:spring-data-rest-webmvc")

浏览应用程序的DEBUG日志,我看到两个线程:

1
2
i. [http-nio-8080-exec-1],
ii [[http-nio-8080-exec-2]

第一个调用AnonymousAuthenticationToken并在@ 15:02:56.731中失败:

1
org.springframework.security.authentication.AnonymousAuthenticationToken

几毫秒后@ 15:02:56.747第二个成功完成:
org.springframework.security.authentication.UsernamePasswordAuthenticationToken

例如

1
2
3
4
5
6
7
8
15:02:56.731 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: 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:02:56.747 [http-nio-8080-exec-2] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@fb774aa3: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER

15:02:56.731 [http-nio-8080-exec-1] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@9324be9, returned: -1
15:02:56.747 [http-nio-8080-exec-2] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@9324be9, returned: 1

15:02:56.731 [http-nio-8080-exec-1] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.integration.internalMessagingAnnotationPostProcessor'
15:02:56.747 [http-nio-8080-exec-2] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Authorization successful

失败的方法:AnonymousAuthenticationToken正在生成异常。

application.yml中的代码段:

1
2
3
4
# Authentication for"user" to the HTTP endpoints
security:
  user:
    password: blahblahblah
  • Spring Book是否首先尝试使用AnonymousAuthenticationToken代码,因为
    application.yml中的"用户"字段为空?

  • 当我将'user'字段更改为真实值时,我的gradle构建在sftp验证中出现错误:

  • 11:57:25.869 [DEBUG] [TestEventLogger] Caused by:
    11:57:25.869 [DEBUG] [TestEventLogger] mapping values are not allowed here
    11:57:25.869 [DEBUG] [TestEventLogger] in 'reader', line 33, column 13:
    11:57:25.880 [DEBUG] [TestEventLogger] password: blahblahblah
    11:57:24.166 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.scanner.ScannerImpl.fetchValue(ScannerImpl.java:871)
    11:57:24.167 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.scanner.ScannerImpl.fetchMoreTokens(ScannerImpl.java:360)
    11:57:24.167 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.scanner.ScannerImpl.checkToken(ScannerImpl.java:226)
    11:57:24.167 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.parser.ParserImpl$ParseBlockMappingKey.produce(ParserImpl.java:558)
    11:57:24.168 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java:158)
    11:57:24.168 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.parser.ParserImpl.checkEvent(ParserImpl.java:143)
    11:57:24.169 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:226)
    11:57:24.169 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:155)
    11:57:24.169 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:231)
    11:57:24.171 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:155)
    11:57:24.171 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.composer.Composer.composeDocument(Composer.java:122)
    11:57:24.175 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.composer.Composer.getNode(Composer.java:84)
    11:57:24.176 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.constructor.BaseConstructor.getData(BaseConstructor.java:104)
    11:57:24.176 [DEBUG] [TestEventLogger] at org.yaml.snakeyaml.Yaml$1.next(Yaml.java:502)
    11:57:24.176 [DEBUG] [TestEventLogger] at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:160)
    11:57:24.176 [DEBUG] [TestEventLogger] at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:138)
    11:57:24.176 [DEBUG] [TestEventLogger] at org.springframework.boot.env.YamlPropertySourceLoader$Processor.process(YamlPropertySourceLoader.java:100)
    11:57:24.181 [DEBUG] [TestEventLogger] at org.springframework.boot.env.YamlPropertySourceLoader.load(YamlPropertySourceLoader.java:57)
    11:57:24.183 [QUIET] [system.out] 11:57:24.183 [DEBUG] [org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor]
    Executing test class
    com.distributedfinance.mbi.payment.repository.ExternalAccountTransferRepositorySpecIT
    11:57:24.184 [DEBUG] [TestEventLogger] at org.springframework.boot.env.PropertySourcesLoader.load(PropertySourcesLoader.java:126)
    11:57:24.184 [DEBUG] [TestEventLogger] at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.loadIntoGroup(ConfigFileApplicationListener.java:381)
    11:57:24.184 [DEBUG] [TestEventLogger] at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:369)
    11:57:24.184 [DEBUG] [TestEventLogger] at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:339)
    11:57:24.184 [DEBUG] [TestEventLogger] at org.springframework.boot.context.config.ConfigFileApplicationListener.addPropertySources(ConfigFileApplicationListener.java:174)
    11:57:24.185 [DEBUG] [TestEventLogger] at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent(ConfigFileApplicationListener.java:144)
    11:57:24.185 [DEBUG] [TestEventLogger] at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent(ConfigFileApplicationListener.java:137)
    11:57:24.185 [DEBUG] [TestEventLogger] at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEvent(ConfigFileApplicationListener.java:126)
    11:57:24.185 [DEBUG] [TestEventLogger] at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:151)
    11:57:24.185 [DEBUG] [TestEventLogger] at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:128)
    11:57:24.186 [DEBUG] [TestEventLogger] at org.springframework.boot.context.event.EventPublishingRunListener.publishEvent(EventPublishingRunListener.java:100)
    11:57:24.186 [DEBUG] [TestEventLogger] at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:59)
    11:57:24.186 [DEBUG] [TestEventLogger] at org.springframework.boot.SpringApplication.run(SpringApplication.java:285)
    11:57:24.186 [DEBUG] [TestEventLogger] at org.springframework.boot.test.SpringApplicationContextLoader.loadContext(SpringApplicationContextLoader.java:103)
    11:57:24.186 [DEBUG] [TestEventLogger] at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:68)
    11:57:24.187 [DEBUG] [TestEventLogger] at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:86)
    11:57:24.187 [DEBUG] [TestEventLogger] ... 24 more

  • 为什么是应用程序。配置为尝试两种身份验证方法?
  • 应该如何配置?
  • Spring Boot自动配置中是否有某些东西可以控制这一点?
  • enter


    绝对错误是在UsernamePasswordAuthenticationFilter之前正在执行AnonymousAuthenticationFilter(在此处检查默认顺序)

    我欠您有关为何在您的情况下如此配置的解释,但我会回答这一问题。

  • Why is the app. configured to try both methods of authentication?
  • 在进行任何身份验证尝试之后,必须执行匿名过滤器,如果没有,则将其执行操作将AuthenticationToken放入SecurityContextHolder中(这意味着所有身份验证尝试均失败)。

    Spring这样做是为了以更一致的方式处理Authentication令牌,否则应询问SecurityContextHolder.getAuthentication!= null,这会使授权机制复杂化,例如询问用户的角色。 >

    摘自Spring文档:

    Note that there is no real conceptual difference between a user who is a€?anonymously authenticateda€? and an unauthenticated user. Spring Security's anonymous authentication just gives you a more convenient way to configure your access-control attributes. Calls to servlet API calls such as getCallerPrincipal, for example, will still return null even though there is actually an anonymous authentication object in the SecurityContextHolder.

    Classes can be authored more robustly if they know the SecurityContextHolder always contains an Authentication object, and never null.

    正在发生的事情是,您正在向不需要身份验证的身份验证的端点发出请求,但是您在请求中包含了凭据。

  • 请求到/ securedEndpoint的凭据,但没有授权标头
  • BasicAuthenticationFilter已执行,但是没有Authorization标头,因此无法验证用户身份。
  • 执行AnonymousAuthenticationFilter,并将AnonymousToken放入SecurityContextHolder中。
  • 授权机制抛出异常,因为请求尝试获取安全资源。
  • ExceptionTranslationFilter捕获此异常并调用AuthenticationEntryPoint
  • AuthenticationEntryPoint重定向到/ login
  • UsernamePassordAuthenticationFilter将被执行并成功验证您的用户。
  • 再次执行AnonymousAuthenticationFilter,但是就像在SecurityContextHolder中有一个Authentication一样,它什么也没做。
  • 授权机制允许用户获取资源。
  • 如果您想要无状态/无会话的后端,并且要随每个请求发送凭据,则必须将UsernamePasswordAuthenticationFilter配置为在所有请求(/)或所有安全端点(/ secure /)中执行。 BasicAuthenticationFilter不是必需的,但您可以让该过滤器。

    提示:您可以使用JWT进行无状态身份验证。

    更新:

    用于在Spring Boot中自定义您的安全配置

    The default security configuration is implemented in SecurityAutoConfiguration and in the classes imported from there (SpringBootWebSecurityConfiguration for web security and AuthenticationManagerConfiguration for authentication configuration which is also relevant in non-web applications). To switch off the Boot default configuration completely in a web application you can add a bean with @EnableWebSecurity. To customize it you normally use external properties and beans of type WebSecurityConfigurerAdapter (e.g. to add form-based login). There are several secure applications in the Spring Boot samples to get you started with common use cases.