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
绝对错误是在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.
正在发生的事情是,您正在向不需要身份验证的身份验证的端点发出请求,但是您在请求中包含了凭据。
如果您想要无状态/无会话的后端,并且要随每个请求发送凭据,则必须将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.