使用 HTTP 标头的 Spring Security
Posted
技术标签:
【中文标题】使用 HTTP 标头的 Spring Security【英文标题】:Spring Security using HTTP headers 【发布时间】:2017-10-22 00:08:05 【问题描述】:我正在尝试为我的 Spring Boot 应用程序添加安全性。我当前的应用程序正在使用 REST 控制器,每次收到 GET
或 POST
请求时,我都会读取 HTTP 标头以检索用户和密码,以便根据我存储的所有用户的属性文件验证它们。我想将其更改为使用 Spring Security,这就是我目前所得到的:
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Bean
protected void configure(HttpSecurity http) throws Exception
http.authorizeRequests()
.antMatchers("/index.html").permitAll()
.antMatchers("/swagger-ui.html").hasRole("ADMIN")
.anyRequest().authenticated();
@Bean
public UserDetailsService userDetailsService()
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("admin").password("password").roles("ADMIN").build());
我如何告诉configure
方法是从标头而不是登录表单中检索用户凭据?
【问题讨论】:
您的意思是使用Authentication
标头进行基本身份验证吗?
你的HTTP头是什么意思,头的名字是什么?
【参考方案1】:
在 spring boot 应用程序中,您可以在 application.properties 中添加以下内容
security.user.name=user
security.user.password=password
它会做剩下的事情,比如从标题和验证中获取它 更多信息请访问https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-security.html
【讨论】:
感谢您的回答,但我有不止一个用户(我只是作为示例发布) 看到这个你可以创建自己的 AuthenticationManager 并添加用户帐户docs.spring.io/spring-boot/docs/current/reference/html/…【参考方案2】:您应该避免使用默认的org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
,因为它会从您的请求参数中获取客户端提供的用户名和密码,而您确实需要从标头中获取它们。
因此,您应该编写自定义 AuthenticationFilter
扩展引用 UsernamePasswordAuthenticationFilter
以更改其行为以满足您的要求:
public class HeaderUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter
public HeaderUsernamePasswordAuthenticationFilter()
super();
this.setFilterProcessesUrl("/**");
this.setPostOnly(false);
/* (non-Javadoc)
* @see org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#obtainPassword(javax.servlet.http.HttpServletRequest)
*/
@Override
protected String obtainPassword(HttpServletRequest request)
return request.getHeader(this.getPasswordParameter());
/* (non-Javadoc)
* @see org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#obtainUsername(javax.servlet.http.HttpServletRequest)
*/
@Override
protected String obtainUsername(HttpServletRequest request)
return request.getHeader(this.getPasswordParameter());
此过滤器示例扩展 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
监听每个请求并从标头而不是 parameters
获取 username
和 password
。
那么你应该这样改变配置,将你的过滤器设置在UsernamePasswordAuthenticationFilter
位置:
@Override
protected void configure(HttpSecurity http) throws Exception
http.addFilterAt(
new HeaderUsernamePasswordAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/index.html").permitAll()
.antMatchers("/swagger-ui.html").hasRole("ADMIN")
.anyRequest().authenticated();
【讨论】:
好的,谢谢,但我现在如何在我的属性文件中读取角色? 请编辑您的帖子,包括您的属性文件结构。您必须构建一个UserDetailsService
,它会从您的文件中读取条目【参考方案3】:
内存中的身份验证可以满足您的目的
@Configuration
@EnableWebMvc
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
auth.inMemoryAuthentication()
.withUser("user1").password("password1").roles("USER")
.and()
.withUser("user2").password("password2").roles("ADMIN");
@Override
protected void configure(HttpSecurity http) throws Exception
http.authorizeRequests().anyRequest().fullyAuthenticated();
http.httpBasic();
【讨论】:
【参考方案4】:最少的代码添加是定义一个过滤器并将其添加到安全配置中,smth like
XHeaderAuthenticationFilter.java
@Component
public class XHeaderAuthenticationFilter extends OncePerRequestFilter
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException
String xAuth = request.getHeader("X-Authorization");
User user = findByToken(xAuth);
if (user == null)
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token invalid");
else
final UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
//need to implement db user validation...
private User findByToken(String token)
if (!token.equals("1234"))
return null;
final User user = new User(
"username",
"password",
true,
true,
true,
true,
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
return user;
SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Override
protected void configure(final HttpSecurity http) throws Exception
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.addFilterBefore(new XHeaderAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
另一种方法是使用spring的AOP
在进入带注释的控制器方法之前定义一些要执行的逻辑的注释
【讨论】:
有趣的例子。在我的代码中,我在过滤器的开头检查是否存在安全上下文,以避免为每个请求访问数据库。当然我也允许创建会话。 @Pino ,是的,那会更有效,请记住,通常在使用标头身份验证时,它与无状态并驾齐驱......所以基本上没有任何会话可以从中获得上下文......但是这段代码是一个最小的,可以大大扩展......以上是关于使用 HTTP 标头的 Spring Security的主要内容,如果未能解决你的问题,请参考以下文章
使用 Spring Security 自定义 Http 授权标头
我可以使用 Spring Security @PreAuthorize 来检查 HTTP 标头吗?
使用 spring-test-mvc 自定义测试 http 标头
无法使用 spring mvc 和 $http 访问 CORS 环境中的自定义标头