好吧,我没有实现PersistentTokenBasedRememberMeServices
,所以我不能使用.logout(request, response, auth)
。但是我使用JdbcTokenRepositoryImpl
以便将PersistentTokenRepository
用于“记住我”功能。
LogoutController:
@Controller
public class LogoutController {
@RequestMapping(value = {"/logout"}, method = RequestMethod.GET)
public String logout() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth != null) {
SecurityContextHolder.getContext().setAuthentication(null);
}
return "redirect:/login?logout";
}
}
安全配置:
@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/playground").hasAnyRole("ROOT", "MODER", "USER")
.antMatchers("/users/**").hasAnyRole("ROOT", "MODER")
.and()
.formLogin().loginPage("/login").loginProcessingUrl("/login").failureHandler(customAuthenticationFailureHandler())
.and()
.rememberMe().rememberMeParameter("remember-me").tokenRepository(persistentTokenRepository()).userDetailsService(userDetailsService)
.and()
.logout().logoutUrl("/logout");
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setPasswordEncoder(passwordEncoder());
authProvider.setUserDetailsService(userDetailsService);
return authProvider;
}
@Bean
public AuthenticationFailureHandler customAuthenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
当我使用“记住我”身份登录时,便无法退出。我猜是因为记住我的功能。我应该添加什么到LogoutController才能进行正确的注销过程?
注意:问题是,如果我仅在注销时使用POST方法,那么它就可以正常工作,但是我想使用GET方法,因此我必须创建一个注销控制器来执行get方法。
答案 0 :(得分:2)
尝试禁用crsf(http.csrf().disable()
)。
spring的安全性注销过滤器中的默认实现为:
if (http.getConfigurer(CsrfConfigurer.class) != null) {
this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
}
else {
this.logoutRequestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(this.logoutUrl, "GET"),
new AntPathRequestMatcher(this.logoutUrl, "POST"),
new AntPathRequestMatcher(this.logoutUrl, "PUT"),
new AntPathRequestMatcher(this.logoutUrl, "DELETE")
);
}
如您所见,您的Csrf是否已启用(即使您改写了protected void configure(HttpSecurity http)
,默认情况下也已启用),那么只有POST
方法有效,如果不是全部的话。
顺便说一句:您确定您的请求到达了LogoutController
,因为我认为它使用的是标准的Spring Security注销机制? (要禁用它,请http.logout().disable()
,与默认情况下启用的csrf相同)
答案 1 :(得分:1)
我设法测试了几种方法,下面是我所得到的:
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
http.csrf().disable()
.deleteCookies("remember-me", "JSESSIONID")
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/")
(我不确定,但是我觉得它不起作用,因为您执行GET请求并使用控制器来控制注销)
首先,在Spring安全配置中为“记住我的cookie”添加一个名称:
rememberMe().rememberMeCookieName("remember-me")
然后在注销控制器中添加以下内容:
String cookieName = "remember-me";
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
response.addCookie(cookie);
这里唯一的问题是您必须手动从persistent_logins表中删除一条记录
(为了获得请求和响应,您只需将它们传递给方法public void logout(HttpServletRequest request, HttpServletResponse response)
您可以在this topic上找到解决方案。
总结上面的所有内容,我可以说,如果您想要一个控制器,则必须自己以编程方式编写所有内容(有人会说这是车轮的重塑)。
仍然可以使用GET请求,但不使用列表的第一和第二位置描述的控制器。
(使用GET请求的后果写在CSRF Documentation中,由于它的无漏洞性,不建议使用GET请求。)
因此,我决定成为我的最爱的最后一件事是借助JS或HTML和CSS使POST请求看起来像GET请求(将其用作链接)。而且,当您使用POST请求时,您就会获得CSRF保护。
我希望这会对某人有所帮助。