Java 基于 Spring Security Token 的认证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42354138/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:04:54  来源:igfitidea点击:

Spring Security Token based Authentication

javaspring-security

提问by Raghavendra

I have a rest api where I am authenticating using spring security Basic Authorization where client sends username and password for each request. Now, I wanted to implement token based authentication where I will send a token in response header when user is authenticated at first. For further requests, client can include that token in the header which will be used to authenticate the user to the resources. I have two authentication providers tokenAuthenticationProvider and daoAuthenticationProvider

我有一个 rest api,我在其中使用 spring 安全基本授权进行身份验证,客户端为每个请求发送用户名和密码。现在,我想实现基于令牌的身份验证,当用户首先通过身份验证时,我将在响应标头中发送一个令牌。对于进一步的请求,客户端可以在标头中包含该令牌,该令牌将用于验证用户对资源的身份。我有两个身份验证提供程序 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);
        }
}

And in daoAuthenticationProvider I am setting custom userDetailsService and authenticating against user login details by fetching it from the database (which is working fine as long as user name and password are passed using Authorization:Basic bGllQXBpVXNlcjogN21wXidMQjRdTURtR04pag== as header)

在 daoAuthenticationProvider 中,我正在设置自定义 userDetailsS​​ervice 并通过从数据库中获取用户登录详细信息进行身份验证(只要使用 Authorization:Basic bGllQXBpVXNlcjogN21wXidMQjRdTURtR04pag== 作为标头传递用户名和密码,它就可以正常工作)

But when I include token in the header using X-AUTH-TOKEN (which is Constants.AUTH_HEADER_NAME), tokenAuthenticationProvider is not being called. I am getting error as

但是,当我使用 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"}

And here is how I am adding authentication providers.

这是我添加身份验证提供程序的方式。

    @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);
} 

Please suggest how can I implement Token based authentication without hurting the current behavior of spring security.

请建议我如何在不损害 spring 安全性当前行为的情况下实现基于令牌的身份验证。

采纳答案by Raghavendra

Here is how I was able to implement token based authentication and basic authentication

这是我如何能够实现基于令牌的身份验证和基本身份验证

SpringSecurityConfig.java

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

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

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");


    }

}

As our CustomBasicAuthenticationFilter has been configured and added as a filter to the spring security,

由于我们的 CustomBasicAuthenticationFilter 已配置并添加为 spring 安全性的过滤器,

Whenever basic authentication is successful the request will be redirected to onSuccessfulAuthentication where we set the token and send it in the response with some header "header-name".

每当基本身份验证成功时,请求将被重定向到 onSuccessfulAuthentication,我们在其中设置令牌并在响应中将其与一些标头“header-name”一起发送。

If "header-name" is sent for further request, then the request will go through TokenAuthenticationFilter first before attempting to try Basic Authentication.

如果为进一步请求发送“header-name”,则在尝试尝试基本身份验证之前,请求将首先通过 TokenAuthenticationFilter。

回答by armandino

You can try setting your custom AuthenticationTokentoken in your authentication filter, for example:

您可以尝试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 );
    }
}