Spring-使用cookie的RESTful身份验证

Bak*_*123 4 cookies spring 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;
    }

}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Set-Cookie: my_cookie=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/
Run Code Online (Sandbox Code Playgroud)

怎么了?

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

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

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

另外,您是否正在使用自定义登录控制器方法?(例如,您的usercontroller是否明确认证用户?)

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

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

不,我只使用Spring Security。至少我是这么认为的... :)

UserController登录方法在做什么?

我更新了我的代码。

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

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

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

对于将要执行类似操作的人,我也建议您阅读这篇出色的文章-https ://dzone.com/articles/secure-rest-services-using :)

rhi*_*nds 5

好的,所以最好的起点是远离自定义的spring控制器进行登录,而仅将其委托给spring安全-这些文档将为您提供有关如何入门的很好的概述- 参见此处开始

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

@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);
}
Run Code Online (Sandbox Code Playgroud)

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

下一个重要的方面是您userDetailsService和身份验证管理器的接线:

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

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

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

花费一些时间让spring-security配置工作并处理简单的登录情况可能是值得的,一旦将所有这些都委派给了spring-security机械,应该更容易地更新配置以将其记住。


回应编辑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”如何知道必须注销哪个用户?

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

  1. 令牌的到期日期呢?默认值为2周,那又如何?我可以设置为令牌永不过期吗?

再次,假设您遵循上面链接中描述的模式,这没关系,因为我们在第一次登录时就使用该cookie,之后您的应用程序将具有一个用于记住身份的“记住我”令牌,因此该cookie实际上就是从这一点上丢弃。