我尝试使用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
。但我有相关的问题:
对于那些会做类似事情的人,我建议你看看这篇好文章 - https://dzone.com/articles/secure-rest-services-using:)
答案 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(您在操作系统中链接到的实施细节的说明)
- 成功登录后,我会收到一封cookie作为回应。要进一步请求,我必须手动添加令牌(客户端),如果是的话 自动添加到服务器端?
醇>
因此,假设您正在登录,然后从您的移动应用程序发出API请求 - 根据文章,您需要在应用程序中使用webview以允许用户登录,一旦完成,WebView将收到来自登录将包括cookie。此时,您关心的只是从cookie中提取令牌 - 之后,不需要cookie。在您的应用程序中,您可以随意保留令牌,只需添加该令牌即可确保在应用程序的每个API请求中提供该令牌 - 文章中的RememberMe实现从API请求标头中提取令牌并对用户进行身份验证。
- 退出怎么样? “Spring”如何知道哪个用户必须注销?
醇>
它不会 - 配置已经设置为无状态,例如它不跟踪登录用户,这是通过存在有效cookie记录的(或者在API请求的情况下,存在我们提取的令牌) - 例如在无状态模式下,检查对应用程序发出的每个请求是否经过身份验证
- 令牌到期日期怎么样?默认是2周,那么什么?我可以设置令牌永不过期吗?
醇>
同样,假设您正在遵循上面链接中描述的模式,这没关系,因为我们只是在首次登录时使用该cookie,之后您的应用程序具有将用于身份验证的记忆我令牌,因此从那时起基本上就丢弃了cookie。
答案 1 :(得分:1)
希望您收到/发送JSESSIONID cookie以及请求。例如,我在使用 RestAssured 时使用this code。