【问题标题】:Spring - RESTful authentication using cookiesSpring - 使用 cookie 的 RESTful 身份验证
【发布时间】:2015-08-19 16:39:43
【问题描述】:

我尝试使用 cookie 实现身份验证(针对我的 android 客户端应用程序),基于这篇文章 -http://automateddeveloper.blogspot.co.uk/2014/03/securing-your-mobile-api-spring-security.html

安全配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final static String TOKEN_STRING = "my_token";
    private final static String COOKIE_STRING = "my_cookie";

    @Autowired
    private UserDetailsService userSvc;
    @Autowired
    private MyTokenBasedRememberMeService tokenSvc;
    @Autowired
    private RememberMeAuthenticationProvider rememberMeProvider;
    @Autowired 
    private MyAuthSuccessHandler authSuccess;
    @Autowired
    private MyAuthFailureHandler authFailure;
    @Autowired
    private MyLogoutSuccessHandler logoutSuccess;


    @Autowired
    protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .userDetailsService(userSvc)
            .passwordEncoder(passwordEncoder());

        auth.authenticationProvider(rememberMeProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

         http
            .authorizeRequests()
                .antMatchers("/register").permitAll()
                .anyRequest().authenticated().and()
            .formLogin()
               .loginPage("/")
               .loginProcessingUrl("/loginendpoint")
               .successHandler(authSuccess)
               .failureHandler(authFailure).and()
            .logout()                           
                .logoutUrl("/logout")
                .logoutSuccess(logoutSuccess)
                .deleteCookies(COOKIE_STRING).and()
            .rememberMe()
                .rememberMeServices(tokenSvc).and()
            .csrf()
                .disable()
            .addFilterBefore(rememberMeAuthenticationFilter(), BasicAuthenticationFilter.class)
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public RememberMeAuthenticationFilter rememberMeAuthenticationFilter() throws Exception {
        return new RememberMeAuthenticationFilter(authenticationManager(), tokenBasedRememberMeService());
    }

    @Bean
    public RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
        return new RememberMeAuthenticationProvider(TOKEN_STRING);
    }

    @Bean
    public MyTokenBasedRememberMeService tokenBasedRememberMeService() {
        MyTokenBasedRememberMeService service = new MyTokenBasedRememberMeService(TOKEN_STRING,
                userSvc);
        service.setAlwaysRemember(true);
        service.setCookieName(COOKIE_STRING);
        return service;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

}

MyTokenBasedRememberMeService:

public class MyTokenBasedRememberMeService extends TokenBasedRememberMeServices {

    private final static String TOKEN_STRING = "my_token";

    public MyTokenBasedRememberMeService(String key, UserDetailsService userDetailsService) {
        super(key, userDetailsService);
    }

    @Override
    protected String extractRememberMeCookie(HttpServletRequest request) {
        String token = request.getHeader(TOKEN_STRING);
        if ((token == null) || (token.length() == 0)) {
            return "";
        }
        return token;
    }
}

不幸的是,在成功登录后,我的 cookie 在客户端是空的:

Set-Cookie: my_cookie=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/

怎么了?

-------编辑 1-------

如果您直接在浏览器中登录,您不会收到任何 cookie(例如在开发工具中)?

我使用邮递员对其进行了测试,但我只收到了 JSESSIONID cookie(没有 my_cookie)。

另外,您是否使用自定义登录控制器方法? (例如,您的用户控制器是否明确地对用户进行身份验证?)

是的,我正在使用自定义登录控制器方法,但我是 Spring Security 的新手,如果可以在没有自定义控制器的情况下完成,我将不胜感激任何解释。我的控制器负责对用户进行身份验证。

如果您没有使用 spring-security 来处理身份验证,那么我怀疑您可能必须自己显式设置 cookie 等

不,我只使用弹簧安全性。至少我是这么认为的...... :)

UserController 登录方法是做什么的?

我更新了我的代码。

-------编辑 2-------

根据@rhinds 的建议和 spring 文档,我纠正了一些事情(上面的代码已更新)。现在我可以登录loginendpoint,登录后我得到my_cookie。但我有相关的问题:

  1. 成功登录后,我收到一个 cookie 作为响应。如果令牌是在服务器端自动添加的,我必须手动添加令牌(客户端)以进一步请求?
  2. 注销怎么办? “Spring”如何知道必须注销哪个用户?
  3. 令牌到期日期如何?默认是2周,然后呢?我可以设置令牌永不过期吗?

对于将做类似事情的人,我建议也看看这篇很棒的文章 - https://dzone.com/articles/secure-rest-services-using :)

【问题讨论】:

  • 如果您直接在浏览器中登录,您不会收到任何 cookie(例如在开发工具中)?
  • 另外,您使用的是自定义登录控制器方法吗? (例如,您的用户控制器是否显式地对用户进行身份验证?) - 如果您没有使用 spring-security 来处理身份验证,那么我怀疑您可能必须自己显式设置 cookie 等。 (如果没有,UserController 登录方法是做什么的?)
  • @rhinds,请看我更新的帖子。
  • 我现在已经更新了我的答案

标签: spring cookies spring-security spring-boot


【解决方案1】:

好的,所以最好的起点是离开用于登录的自定义 spring 控制器,并将其委托给 spring security - 文档将为您提供如何开始的很好的概述 - @987654321 @

从链接的文章中,如果您查看配置代码:

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

.formLogin() 调用下的部分告诉 spring-security 监听登录尝试的端点 - 例如如果我有这个配置和POST 到端点/loginprocess 那么Spring-security 将拦截它并使用身份验证管理器来处理提交的表单(期望用户名和密码字段等)。

下一个重要的部分是将您的userDetailsService 和身份验证管理器连接在一起:

 @Override protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
     auth
        .userDetailsService(userDetailsServiceImpl)
        .passwordEncoder(bCryptPasswordEncoder());
     auth.authenticationProvider(rememberMeAuthenticationProvider);
 }

这为 spring-security 提供了在给定登录尝试的情况下尝试加载用户对象的方法 - 只要您的类实现 UserDetailsService,那么 spring security 就应该拥有它所需要的一切。

假设一切都正确,那么您应该能够删除自定义登录控制器方法,定义一个loginProcessingUrl,然后对其定义POST,spring-security 应该启动并(尝试)处理它。

花一些时间让 spring-security 配置工作并处理简单的登录情况可能是值得的,一旦这一切都被委托给 spring-security 机器,更新配置以连接到 RememberMe 端应该更容易东西。


对修改 2 的回应

我假设您根据此处的一般方法遵循链接文章中的实现细节:http://automateddeveloper.blogspot.co.uk/2014/03/securing-your-api-for-mobile-access.html(您在操作中链接到的实现细节的解释)

  1. 成功登录后,我收到一个 cookie 作为响应。对于进一步的请求,我必须手动添加令牌(客户端),如果它是 自动添加到服务器端?

因此,假设您正在登录,然后从您的移动应用发出 API 请求 - 根据文章,您需要在应用中使用 webview 以允许用户登录,一旦他们完成,WebView 将收到来自登录,其中将包含 cookie。此时,您所关心的只是从 cookie 中提取令牌——之后,就不再需要 cookie。在您的应用程序中,您可以随心所欲地保留令牌,并且只需添加它以确保在您从应用程序发出的每个 API 请求中提供该令牌 - 文章中的 RememberMe 实现从 API 请求标头中提取令牌并验证用户。

  1. 注销怎么办? “Spring”如何知道必须注销哪个用户?

不会 - 配置已将 spring 设置为无状态,例如它不跟踪登录的用户,这是由有效 cookie 的存在(或者在 API 请求的情况下,我们提取的令牌的存在)记录的 - 例如。在无状态模式下,对应用程序发出的每个请求都会被检查以查看它是否经过身份验证

  1. 令牌到期日期如何?默认是2周,然后呢?我可以设置令牌永不过期吗?

再次,假设您遵循上面链接中描述的模式,这并不重要,因为我们只是在第一次登录时使用该 cookie,之后您的应用程序有一个记住我的令牌,将用于身份验证,所以从那时起,cookie 基本上被丢弃了。

【讨论】:

  • 非常感谢...我根据您的建议更改了一些代码。请参阅我更新的帖子。
  • 如果我将添加到每个请求(作为参数/正文)令牌,我怎样才能在服务器端进行验证?
  • 您的 MyTokenBasedRememberMeService 实现 - 您已经覆盖了 extractRememberMeCookie() 方法,Spring 通常会使用它从 cookie 中获取令牌以验证请求 - 在这种情况下,您从HTTP 标头(在此示例中当前称为“my_token”) - 因此 Spring 的 RememberMe 功能将处理此处的所有内容。
  • 在添加my_token 请求它可以工作后,非常感谢:)
  • 抱歉,你是对的 - 到期时间确实对记住我有影响。一个更大的问题是你确定你想要拥有永不过期的记忆令牌 - 其他选项是你保持与迄今为止采用的类似方法并且只是在活动中刷新令牌(例如,如果用户没有使用应用程序 2 周,那么他们必须再次登录,但假设使用更频繁,只需刷新令牌 - 类似于现在 cookie 的工作方式)。或者,如果您真的想删除过期,请查看我们已经覆盖以获取令牌的 TokenBasedRememberMeServices 源。
【解决方案2】:

希望您正在接收/发送 JSESSIONID cookie 以及请求。例如,我在使用 RestAssured 时使用this code

【讨论】:

  • 谢谢,但我想做点不同的。正如本文中所写。
猜你喜欢
  • 2013-05-06
  • 2012-02-04
  • 2020-11-13
  • 2020-01-04
  • 1970-01-01
  • 1970-01-01
  • 2012-06-05
  • 2017-09-14
相关资源
最近更新 更多