Spring - 使用cookie的RESTful身份验证

时间:2015-08-19 16:39:43

标签: spring cookies spring-security spring-boot

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

SecurityConfig:

@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来处理身份验证,那么我怀疑您可能必须自己明确设置cookie等

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

  

UserController登录方法在做什么?

我更新了我的代码。

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

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

  1. 成功登录后,我会收到一封cookie作为回应。要进一步请求,我必须手动添加令牌(客户端),如果它自动添加到服务器端?
  2. 退出怎么样? “Spring”如何知道哪个用户必须注销?
  3. 令牌到期日期怎么样?默认是2周,那么什么?我可以设置令牌永不过期吗?
  4. 对于那些会做类似事情的人,我建议你看看这篇好文章 - https://dzone.com/articles/secure-rest-services-using:)

2 个答案:

答案 0 :(得分:4)

好的,所以最好的起点是远离自定义弹簧控制器进行登录,并将其委托给弹簧安全 - 文档将为您提供如何开始使用它的非常好的概述 - {{ 3}}

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

@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安全应该具备它所需要的一切。

假设一切正确,那么您应该能够删除自定义登录控制器方法,为其定义loginProcessingUrl然后POST并且弹簧安全性应该启动并且(尝试)处理它

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

对编辑2的回应

我假设您正在根据此处的一般方法跟踪链接文章中的实施细节:see here to get started(您在操作系统中链接到的实施细节的说明)

  
      
  1. 成功登录后,我会收到一封cookie作为回应。要进一步请求,我必须手动添加令牌(客户端),如果是的话   自动添加到服务器端?
  2.   

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

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

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

  
      
  1. 令牌到期日期怎么样?默认是2周,那么什么?我可以设置令牌永不过期吗?
  2.   

同样,假设您正在遵循上面链接中描述的模式,这没关系,因为我们只是在首次登录时使用该cookie,之后您的应用程序具有将用于身份验证的记忆我令牌,因此从那时起基本上就丢弃了cookie。

答案 1 :(得分:1)

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