2017-03-19 61 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標題。難道我做錯了什麼? –

相關問題