【发布时间】:2017-08-10 17:43:06
【问题描述】:
我正在为我的应用程序编写一个客户端。 Spring 堆栈是 Spring 4 和 Spring Security 4(主要部分)。
我尝试通过以下方式从我的应用程序中注销:
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> entity = new HttpEntity<>("_csrf=" + csrfToken,
httpHeaders);
restTemplate.postForEntity(appUrl + "/logout", entity, String.class);
RestTemplate 对象是通过以下方式创建的(当然是在登录之前):
new RestTemplate(new HttpComponentsClientHttpRequestFactory())
但我在服务器上得到以下异常:
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported at
org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:207) at
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:374) at
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:314) at
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:61) at
org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:352)
当我尝试登录应用程序时遇到以下异常。唯一的 我设法做到这一点的方法是获取登录页面并从那里获取 CSRF 令牌。我尝试通过以下方式从服务器获取令牌并将其返回给客户端:
@RequestMapping(value = "/api/csrf", method = RequestMethod.GET)
public String csrf(HttpServletRequest httpServletRequest) {
return ((CsrfToken) httpServletRequest.getAttribute(CsrfToken.class.getName())).getToken();
}
但是有了这个令牌,我总是遇到同样的异常。
现在我想至少以任何方式实现注销,但与RestTemplate 正确登录相关的注意事项也值得赞赏。谢谢!
更新:添加安全配置
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final DataSource dataSource;
private final UserDetailsService splittingRolesUserDetails;
private final AccessDeniedHandler accessDeniedHandler;
@Autowired
public SecurityConfig(DataSource dataSource, UserDetailsService splittingRolesUserDetails,
AccessDeniedHandler accessDeniedHandler) {
this.dataSource = dataSource;
this.splittingRolesUserDetails = splittingRolesUserDetails;
this.accessDeniedHandler = accessDeniedHandler;
}
// overrides role prefix in case .access() in httpSecurity configuration
// just because it is needed in the task. hasRole() won't work
// as there are used different voters in AffirmativeBased.
// link to the related issue on GitHub:
// https://github.com/spring-projects/spring-security/issues/3701
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.authenticationProvider(authenticationProvider())
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select user_name, password, true from user where username=?");
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(splittingRolesUserDetails);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
.antMatchers("/login/**").permitAll()
.antMatchers("/api/csrf").permitAll()
.antMatchers("/api/ticket/event**").access("hasRole('" + Role.BOOKING_MANAGER.toString() + "')")
.anyRequest().access("hasRole('" + Role.REGISTERED_USER.toString() + "')")
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/event")
.permitAll()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.accessDeniedPage("/403")
.and()
.rememberMe()
.userDetailsService(splittingRolesUserDetails);
}
}
【问题讨论】:
-
登录时出现什么异常,您是如何登录的,您是否保存了cookie?当您请求注销 url 时,您是否将 cookie 添加到标头?>“当我尝试登录应用程序时出现以下异常”
-
@chaoluo 对于登录,我遇到了与注销时相同的异常,我没有保存 cookie。执行注销时,我正在设置 X-CSRF-TOKEN 标头(尽管据我了解这里不需要),添加“_csrf =”表单参数并设置 Content-Type=application/x-www-form-urlencoded。
-
Http 是一个无状态协议,如果你想实现自己的客户端,你应该使用 cookie(例如 JSESSIONID) 请求。但是为什么你不为你的客户端改变另一个身份验证,(例如 HTTP 基本/承载身份验证)?
-
@chaoluo 我正在使用这个调用创建一个 RestTemplate 对象:new RestTemplate(new HttpComponentsClientHttpRequestFactory())。而且我能够登录到应用程序并执行一些请求。所以我来宾这是添加 JSESSIONID 的事情。抱歉,我不太明白如何更改客户端的身份验证?
-
服务器也应该做一些更改以支持基本身份验证。
标签: spring spring-mvc spring-security csrf resttemplate