从 Spring Boot 中的基本身份验证中删除 WWW-authenticate 标头
Posted
技术标签:
【中文标题】从 Spring Boot 中的基本身份验证中删除 WWW-authenticate 标头【英文标题】:Remove WWW-authenticate header from Basic authentication in Spring Boot 【发布时间】:2016-03-21 15:44:29 【问题描述】:我正在使用 SpringBoot 设计一个 REST API。与此同时,我正在构建一个使用该 API 的 SPA。
出于安全考虑,我选择了易于设置的基本身份验证。我现在正面临 401 挑战问题。当我的 SPA 向我的 API 发出请求时,如果身份验证失败,浏览器会显示我想要摆脱的经典登录弹出窗口。
我阅读了here 和there,当 401 响应还包含一个值为“基本”的 WWW-authenticate 标头时,浏览器会显示此弹出窗口。
所以要删除它,根据 Spring Security,我将提供我自己的 AuthenticationEntryPoint。有点像 Spring Boot 默认情况下的it's done。我试过这个但它不起作用,我在运行时得到一个堆栈。有什么想法吗?
这是我的代码
@Order(SecurityProperties.BASIC_AUTH_ORDER)
@Configuration
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity http) throws Exception
AuthenticationEntryPoint entryPoint = new CustomAuthenticationEntryPoint();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.regexMatchers("/api/.*").authenticated().and()
.httpBasic().authenticationEntryPoint(entryPoint).and()
.exceptionHandling().authenticationEntryPoint(entryPoint).and()
.csrf().disable();
这是我在使用mvn spring-boot:run
运行时得到的堆栈
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:467)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:296)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:838)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
at org.springframework.boot.SpringApplication.doRun(SpringApplication.java:347)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:295)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1112)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1101)
at org.htulipe.WishlistApplication.main(WishlistApplication.java:44)
... 6 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
... 26 more
Caused by: org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built
at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:44)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain(WebSecurityConfiguration.java:105)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerBySpringCGLIB$$7d149fcb.CGLIB$springSecurityFilterChain$6(<generated>)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerBySpringCGLIB$$7d149fcb$$FastClassBySpringCGLIB$$208d4287.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:318)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerBySpringCGLIB$$7d149fcb.springSecurityFilterChain(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
... 27 more
【问题讨论】:
【参考方案1】:发现问题,我在问题中发布的堆栈之前发送了另一个堆栈。堆栈抱怨我的 AuthenticationEntryPoint 缺少领域名称。为了记录,这里是一个工作示例:
public class CustomAuthenticationEntryPoint extends BasicAuthenticationEntryPoint
public CustomAuthenticationEntryPoint()
this.setRealmName("Yolo");
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException
response.setHeader("WWW-Authenticate", "FormBased");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
【讨论】:
另一种解决方案不是扩展BasicAuthenticationEntryPoint
,而是实现AuthenticationEntryPoint
。他们在BasicAuthenticationEntryPoint
中没有做任何特别的事情,请参阅git.io/vS31k。【参考方案2】:
如果有人遇到类似的问题/挑战,我会发布此信息。
在您的 AuthenticationServerConfigurerAdapter 类中允许表单身份验证,如下所示:
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception
oauthServer.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
这将允许您在表单中发送 client_id 和 client_secret。另外,请确保您提交的表单带有content-type:application/x-www-form-urlencoded
标头。
【讨论】:
【参考方案3】:有一个更简单的选择。只需将 Http401AuthenticationEntryPoint 添加到您的 httpSecurity。
http.exceptionHandling()
.authenticationEntryPoint(new Http401AuthenticationEntryPoint("FormBased"));
【讨论】:
你有xml配置的例子吗?以上是关于从 Spring Boot 中的基本身份验证中删除 WWW-authenticate 标头的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot webservice (REST) - 如何将 JUnit 5 测试从基本身份验证更改为 OAuth2 (Keycloak)
Spring Boot with Spring Boot:将基本身份验证与JWT令牌身份验证相结合[复制]
如何在 Spring Boot 中使用 RESTful 和基本身份验证
在 Spring Security(spring-boot 项目)中使用 ldap 凭据进行 Http 基本身份验证以保护休息服务调用