相同 API 的 Spring Security Basic Auth 和 Form 登录
Posted
技术标签:
【中文标题】相同 API 的 Spring Security Basic Auth 和 Form 登录【英文标题】:Spring security Basic Auth and Form login for the same API 【发布时间】:2019-01-20 05:22:13 【问题描述】:我想通过两种身份验证机制访问我的所有 API,基本身份验证和表单登录。我知道存在一些问题,但是答案对我不起作用,而且我的用例有点不同。
我的配置:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig
@Configuration
@Order(1)
public static class SecurityConfigBasicAuth extends WebSecurityConfigurerAdapter
final private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
public SecurityConfigBasicAuth(RestAuthenticationEntryPoint restAuthenticationEntryPoint,
@Qualifier("customUserDetailsService") UserDetailsService userDetailsService)
this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
this.userDetailsService = userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
// @Bean authenticationProvider()
// @Bean passwordEncoder()
@Override
protected void configure(HttpSecurity http) throws Exception
http.authorizeRequests().anyRequest().authenticated()
.and()
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.formLogin().disable()
.logout().disable();
@Configuration
public static class SecurityConfigFormLogin extends WebSecurityConfigurerAdapter
final private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
final private RestfulSavedRequestAwareAuthenticationSuccessHandler restfulSavedRequestAwareAuthenticationSuccessHandler;
final private CustomAuthenticationProvider customAuthenticationProvider;
@Autowired
public SecurityConfigFormLogin(RestAuthenticationEntryPoint restAuthenticationEntryPoint,
RestfulSavedRequestAwareAuthenticationSuccessHandler restfulSavedRequestAwareAuthenticationSuccessHandler,
CustomAuthenticationProvider hashAuthenticationProvider)
this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
this.restfulSavedRequestAwareAuthenticationSuccessHandler = restfulSavedRequestAwareAuthenticationSuccessHandler;
this.customAuthenticationProvider = customAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
auth.authenticationProvider(customAuthenticationProvider);
@Override
protected void configure(HttpSecurity http) throws Exception
http.authorizeRequests().anyRequest().authenticated()
.and()
.cors()
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.csrf().disable()
.httpBasic().disable()
.formLogin()
.usernameParameter("id1")
.passwordParameter("Id2")
.loginProcessingUrl("/test/login")
.successHandler(restfulSavedRequestAwareAuthenticationSuccessHandler)
.failureHandler(myFailureHandler())
.and()
.logout();
// @Bean myFailureHandler()
如您所见,我定义了两个“WebSecurityConfigurerAdapters”,一个用于基本身份验证,一个用于表单登录。表单登录与 REST 兼容(不重定向,但提供 HTTP 响应)。
问题如下:加载的第一个“WebSecurityConfigurerAdapter”工作并覆盖第二个。上面的示例可以使用基本身份验证,但我无法登录 POST '/test/login',我得到:
"timestamp": 1534164906450,
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/test/login"
更新已修复:关键是使用“requestMatchers()”,请参阅答案部分以获取解决方案(如 jzheaux 所建议)
【问题讨论】:
在同一端点上配置两组 Web 安全过滤器不会有任何好处。我唯一可以建议的是使用不同的 URL,一个用于表单登录,一个用于 BASIC auth - 一旦通过身份验证,那么您应该能够使用会话。 我在做什么不同于:github.com/spring-projects/spring-security-javaconfig/blob/…?除了没有定义 url 路径。 使用不同的 URL 是什么意思?两种身份验证都会产生特定的角色,在这些角色中他们可以访问相同的 API 这正是您正在做的不同之处,以及为什么它不起作用。 如果你真的想要两个不同的过滤器链,那么先看看http.requestMatchers()。这将允许您通过 url 相当干净地拆分配置。您(可能)在当前配置中遇到的问题是,它们并没有精确地覆盖它们彼此合并(如果您不执行我上面推荐的操作)。我很想亲自尝试一下一个做 httpBasic 和另一个做 httpBasic().disable 的合并配置。 【参考方案1】:好的,我就是这样解决这个问题的:
我将基本身份验证配置配置为:
protected void configure(HttpSecurity http) throws Exception
http.requestMatchers()
.antMatchers("/api/**")
.and()
.cors()
.and()
.csrf().disable()
.httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and();
如果您不希望基本身份验证返回带有新 JSESSIONID 的新 cookie,请添加:
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.sessionFixation()
.migrateSession()
Form 登录配置为:
protected void configure(HttpSecurity http) throws Exception
http.requestMatchers()
.antMatchers(HttpMethod.POST, "/test/login")
.and()
.cors()
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.formLogin()
.usernameParameter("id1")
.passwordParameter("id2")
.loginProcessingUrl("/test/login")
.successHandler(authenticationSuccessHandler)
.failureHandler(myFailureHandler())
.and()
.logout();
现在,我可以通过表单登录配置进行身份验证,并使用 cookie 会话 ID 调用 /api/**(在基本身份验证配置中配置)。当然,我也可以只使用 Basic Auth 身份验证。
【讨论】:
以上是关于相同 API 的 Spring Security Basic Auth 和 Form 登录的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security + MVC:相同的@RequestMapping,不同的@Secured
spring security oauth2 禁用基于 jsessionid 的会话
grails spring security rest /api/login 401 Unauthorized
使用 Spring Security 保护 REST API