Spring Security - 基于令牌的 API 身份验证和用户/密码身份验证

2024-05-04

我正在尝试创建一个主要使用 Spring 提供 REST API 的 Web 应用程序,并尝试配置安全方面。

我正在尝试实现这种模式:https://developers.google.com/accounts/docs/MobileApps https://developers.google.com/accounts/docs/MobileApps(谷歌已经完全改变了该页面,所以不再有意义 - 请参阅我在这里提到的页面:http://web.archive.org/web/20130822184827/https://developers.google.com/accounts/docs/MobileApps http://web.archive.org/web/20130822184827/https://developers.google.com/accounts/docs/MobileApps)

这是我需要完成的任务:

  • Web应用程序具有简单的登录/注册表单,可以与普通的spring用户/密码身份验证一起使用(之前已经使用dao/authenticationmanager/userdetailsservice等完成了此类操作)
  • REST api 端点是无状态会话,每个请求都根据请求提供的令牌进行身份验证

(例如,用户使用普通表单登录/注册,webapp 提供带有令牌的安全 cookie,然后可以在以下 API 请求中使用该令牌)

我有一个正常的身份验证设置,如下所示:

@Override protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf()
            .disable()
        .authorizeRequests()
            .antMatchers("/resources/**").permitAll()
            .antMatchers("/mobile/app/sign-up").permitAll()
            .antMatchers("/v1/**").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/")
            .loginProcessingUrl("/loginprocess")
            .failureUrl("/?loginFailure=true")
            .permitAll();
}

我正在考虑添加一个预身份验证过滤器,该过滤器检查请求中的令牌,然后设置安全上下文(这是否意味着将跳过正常的后续身份验证?),但是,超出了我所拥有的正常用户/密码没有对基于令牌的安全性做太多事情,但根据其他一些示例,我提出了以下内容:

安全配置:

@Override protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
                .disable()
            .addFilter(restAuthenticationFilter())
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint()).and()
                .antMatcher("/v1/**")
            .authorizeRequests()
                .antMatchers("/resources/**").permitAll()
                .antMatchers("/mobile/app/sign-up").permitAll()
                .antMatchers("/v1/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/")
                .loginProcessingUrl("/loginprocess")
                .failureUrl("/?loginFailure=true")
                .permitAll();
    }

我的自定义休息过滤器:

public class RestAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public RestAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }

    private final String HEADER_SECURITY_TOKEN = "X-Token"; 
    private String token = "";


    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        this.token = request.getHeader(HEADER_SECURITY_TOKEN);

        //If we have already applied this filter - not sure how that would happen? - then just continue chain
        if (request.getAttribute(FILTER_APPLIED) != null) {
            chain.doFilter(request, response);
            return;
        }

        //Now mark request as completing this filter
        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

        //Attempt to authenticate
        Authentication authResult;
        authResult = attemptAuthentication(request, response);
        if (authResult == null) {
            unsuccessfulAuthentication(request, response, new LockedException("Forbidden"));
        } else {
            successfulAuthentication(request, response, chain, authResult);
        }
    }

    /**
     * Attempt to authenticate request - basically just pass over to another method to authenticate request headers 
     */
    @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        AbstractAuthenticationToken userAuthenticationToken = authUserByToken();
        if(userAuthenticationToken == null) throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
        return userAuthenticationToken;
    }


    /**
     * authenticate the user based on token, mobile app secret & user agent
     * @return
     */
    private AbstractAuthenticationToken authUserByToken() {
        AbstractAuthenticationToken authToken = null;
        try {
            // TODO - just return null - always fail auth just to test spring setup ok
            return null;
        } catch (Exception e) {
            logger.error("Authenticate user by token error: ", e);
        }
        return authToken;
    }

上面的内容实际上会导致应用程序启动时出现错误:authenticationManager must be specified谁能告诉我如何最好地做到这一点 - pre_auth 过滤器是做到这一点的最佳方法吗?


EDIT

我写下了我的发现以及如何使用 Spring-security(包括代码)实现标准令牌实现(不是 OAuth)

问题和方法/解决方案概述 https://automateddeveloper.blogspot.co.uk/2014/03/securing-your-api-for-mobile-access.html

使用 Spring-security 实施解决方案 https://automateddeveloper.blogspot.co.uk/2014/03/securing-your-mobile-api-spring-security.html

希望它能帮助其他一些人..


我相信你提到的错误只是因为AbstractAuthenticationProcessingFilter您正在使用的基类需要一个AuthenticationManager。如果您不打算使用它,您可以将其设置为无操作,或者只是实施Filter直接地。如果你的Filter可以验证请求并设置SecurityContext那么通常会跳过下游处理(这取决于下游过滤器的实现,但我在您的应用程序中没有看到任何奇怪的东西,所以它们可能都是这样的)。

如果我是你,我可能会考虑将 API 端点放在一个完全独立的过滤器链中(另一个WebSecurityConfigurerAdapter豆角,扁豆)。但这只会让事情变得更容易阅读,而不一定是至关重要的。

您可能会发现(如评论中所建议的)您最终会重新发明轮子,但尝试并没有什么坏处,并且您可能会在此过程中了解有关 Spring 和 Security 的更多信息。

添加:github方法 https://help.github.com/articles/creating-an-access-token-for-command-line-use非常有趣:用户只需在基本身份验证中使用令牌作为密码,服务器不需要自定义过滤器(BasicAuthenticationFilter很好)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Security - 基于令牌的 API 身份验证和用户/密码身份验证 的相关文章

随机推荐