基于 Spring Security 令牌的身份验证
Posted
技术标签:
【中文标题】基于 Spring Security 令牌的身份验证【英文标题】:Spring Security Token based Authentication 【发布时间】:2017-07-10 07:08:32 【问题描述】:我有一个 rest api,我在其中使用 spring security 基本授权进行身份验证,客户端为每个请求发送用户名和密码。 现在,我想实现基于令牌的身份验证,当用户首先通过身份验证时,我将在响应标头中发送一个令牌。对于进一步的请求,客户端可以在标头中包含该令牌,该令牌将用于对用户进行资源身份验证。我有两个身份验证提供程序 tokenAuthenticationProvider 和 daoAuthenticationProvider
@Component
public class TokenAuthenticationProvider implements AuthenticationProvider
@Autowired
private TokenAuthentcationService service;
@Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
final HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
final String token = request.getHeader(Constants.AUTH_HEADER_NAME);
final Token tokenObj = this.service.getToken(token);
final AuthenticationToken authToken = new AuthenticationToken(tokenObj);
return authToken;
@Override
public boolean supports(final Class<?> authentication)
return AuthenticationToken.class.isAssignableFrom(authentication);
在 daoAuthenticationProvider 中,我正在设置自定义 userDetailsService 并通过从数据库中获取用户登录详细信息对其进行身份验证(只要使用 Authorization:Basic bGllQXBpVXNlcjogN21wXidMQjRdTURtR04pag== 作为标头传递用户名和密码,它就可以正常工作)
但是当我使用 X-AUTH-TOKEN(即 Constants.AUTH_HEADER_NAME)在标头中包含令牌时,不会调用 tokenAuthenticationProvider。我收到错误
"timestamp":1487626368308,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/find"
这就是我添加身份验证提供程序的方式。
@Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception
final UsernamePasswordAuthenticationProvider daoProvider = new
UsernamePasswordAuthenticationProvider(this.service, this.passwordEncoder());
auth.authenticationProvider(this.tokenAuthenticationProvider);
auth.authenticationProvider(daoProvider);
请建议我如何在不损害 spring 安全性的当前行为的情况下实现基于令牌的身份验证。
【问题讨论】:
您有不同的方法可以做到这一点,您可以直接在每个过滤器上@Autowired 提供程序,或者在一个身份验证管理器中设置该提供程序,并在两个过滤器中使用它。当然,你必须在 Spring Security FilterChain 中设置这两个 Filter。 您已经设置了两次 authenticationProvider,第二个 daoProvider 会不会覆盖第一个 tokentAuthenticationProvider,这正是没有运行的类? @ChrisZ 在下面查看我的答案。它对我有用 【参考方案1】:这是我能够实现基于令牌的身份验证和基本身份验证的方法
SpringSecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception
auth.userDetailsService(this.participantService).passwordEncoder(this.passwordEncoder());
@Override
protected void configure(final HttpSecurity http) throws Exception
//Implementing Token based authentication in this filter
final TokenAuthenticationFilter tokenFilter = new TokenAuthenticationFilter();
http.addFilterBefore(tokenFilter, BasicAuthenticationFilter.class);
//Creating token when basic authentication is successful and the same token can be used to authenticate for further requests
final CustomBasicAuthenticationFilter customBasicAuthFilter = new CustomBasicAuthenticationFilter(this.authenticationManager() );
http.addFilter(customBasicAuthFilter);
TokenAuthenticationFilter.java
public class TokenAuthenticationFilter extends GenericFilterBean
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException
final HttpServletRequest httpRequest = (HttpServletRequest)request;
//extract token from header
final String accessToken = httpRequest.getHeader("header-name");
if (null != accessToken)
//get and check whether token is valid ( from DB or file wherever you are storing the token)
//Populate SecurityContextHolder by fetching relevant information using token
final User user = new User(
"username",
"password",
true,
true,
true,
true,
authorities);
final UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
CustomBasicAuthenticationFilter.java
@Component
public class CustomBasicAuthenticationFilter extends BasicAuthenticationFilter
@Autowired
public CustomBasicAuthenticationFilter(final AuthenticationManager authenticationManager)
super(authenticationManager);
@Override
protected void onSuccessfulAuthentication(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response, final Authentication authResult)
//Generate Token
//Save the token for the logged in user
//send token in the response
response.setHeader("header-name" , "token");
由于我们的 CustomBasicAuthenticationFilter 已被配置并作为过滤器添加到 spring security,
只要基本身份验证成功,请求就会被重定向到 onSuccessfulAuthentication,我们在其中设置令牌并在响应中发送带有一些标头“header-name”的令牌。
如果为进一步的请求发送“header-name”,则请求将首先通过 TokenAuthenticationFilter,然后再尝试尝试基本身份验证。
【讨论】:
所以即使令牌认证成功,它也会尝试基本认证对吧?因为那是另一个过滤器。有什么意义呢?他每次都需要发送用户名/密码吗? @selman 他不需要每次都发送用户名/密码。BasicAuthenticationFilter
以这种方式实现,如果请求没有基本的身份验证标头,它只会传递到下一个过滤器。因此,实际上只有在此请求同时具有令牌标头和基本身份验证标头的情况下,这两个过滤器才适用于同一请求。在这种情况下,基本身份验证标头获胜(因为它是最后一个)。
@selman 如果有一个有效的令牌附加到带有令牌头的请求。它不通过基本的身份验证过滤器。有两种情况会执行两个过滤器 1. 如果请求不包含令牌头 2. 如果令牌无效/过期将启动基本身份验证过滤器,您可以在可以使用的基本身份验证过滤器中设置令牌进一步的请求。
@Raghavendra 成功登录后,令牌去哪里,如何传递令牌,检索令牌,是在 cookie 标头JSESSION : token
还是在请求标头AUTHORIZATION: token
,我如何操纵身份验证服务器在哪里给我令牌,如何处理该令牌【参考方案2】:
您可以尝试在身份验证过滤器中设置自定义 AuthenticationToken
令牌,例如:
public class AuthenticationFilter extends GenericFilterBean
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
final String authTokenHeader = ((HttpServletRequest)request).getHeader(Constants.AUTH_HEADER_NAME);
if (authTokenHeader != null)
SecurityContextHolder.getContext().setAuthentication(createAuthenticationToken(authTokenHeader));
chain.doFilter( request, response );
【讨论】:
您好,谢谢您的回答。我实现了类似于您提到的方式的解决方案。我很快就会发布解决方案。以上是关于基于 Spring Security 令牌的身份验证的主要内容,如果未能解决你的问题,请参考以下文章
Grails Spring Security REST得到403禁止
带有 JWT 令牌的 Spring Security 和 Websocket
如何在 Spring Security 中将令牌转换为身份验证?