2017-03-19 60 views
0

我正在为我的应用程序编写客户端。 Spring堆栈是Spring 4和Spring Security 4(主要部分)。在Spring Security应用程序中使用其余模板注销

我尝试从我通过以下方式申请注销:

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) 

我得到以下excep当我试图登录的应用程序。我设法做的唯一的 方法是获取登录页面并从那里获取CSRF令牌。我试图从以下方式从服务器获取令牌,并将其返回给客户端:

@RequestMapping(value = "/api/csrf", method = RequestMethod.GET) 
public String csrf(HttpServletRequest httpServletRequest) { 
    return ((CsrfToken) httpServletRequest.getAttribute(CsrfToken.class.getName())).getToken(); 
} 

但有了这个令牌我得到相同的异常所有的时间。

现在我想至少以任何方式实现注销,但与RestTemplate正确登录相关的笔记也是值得赞赏的。谢谢!

UPDATE:增加安全配置

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); 
    } 
} 
+0

当您登录并且您是如何登录的,您是否保存了cookie?并且当你请求注销url时,你是否将cookie添加到头部?>“当我尝试登录应用程序时,我收到以下异常” – chaoluo

+0

@chaoluo对于登录,我收到了与注销相同的异常,我没有保存cookie。在执行注销时,我正在设置X-CSRF-TOKEN头(虽然这里不需要,据我了解),添加“_csrf =”表单参数并设置Content-Type = application/x-www-form-urlencoded。 –

+0

Http是一个无状态协议,如果你想实现你自己的客户端,你应该使用cookie(例如JSESSIONID)来请求。但为什么不改变客户端的其他验证方式(例如HTTP基本/承载验证)? – chaoluo

回答

0

无需从没有固定一个端点,这对矛盾该令牌首先使用的原则,把你的令牌。您可以通过添加给你的配置与HTTP只能访问一个cookie存储您的令牌:

.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); 

然后,您可以从一个叫XSRF-TOKEN cookie中检索它。

+0

感谢您的答案。但我不确定我是否正确。在我看来,csrf令牌用于保护服务器免受未经授权的请求。为什么应该像浏览器或RestTemplate对象那样为这个目的保护端点?另外我的配置和你自己的建议有什么不同?据我所知,csrf令牌无论如何都将被验证,但这次它将被存储在具有其他名称的cookie中。 –

+0

我认为你得到的错误的csrf事情。 csrf令牌用于防止利用服务器端会话的跨站点请求伪造,并试图模仿请求窃取信息或造成损害。如果令牌可通过终端使用,那么您很容易受到csrf攻击。 Cookie只能由您的应用访问。 –

+0

谢谢你的解释!我想我应该在这里深入挖掘。但我试过你的解决方案,虽然我可以使用我的/ api/csrf方法使登录更加准确,但它似乎不适用于注销。我犯了同样的错误。不,我设置了X-XSRF-TOKEN标题。难道我做错了什么? –

相关问题