Para activar la Basic Html, simplemente creamos un conjunto de clases de esta forma:
@EnableWebSecurity @Configuration @EnableSpringHttpSession public class SecurityConfig extends WebSecurityConfigurerAdapter { @Value("${com.pmj.dyo.configuration.security.user}") private String user; @Value("${com.pmj.dyo.configuration.security.password}") private String password; @Value("${com.pmj.dyo.configuration.csrf.login}") private String loginPage; @Value("${com.pmj.dyo.configuration.csrf.allow}") private String[] patterns; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers(patterns).permitAll().and().csrf().disable().httpBasic().and() .addFilterAfter(csrfFilter(patterns), FilterSecurityInterceptor.class) .addFilterAfter(new CsrfGrantingFilter(loginPage), CsrfFilter.class); } private Filter csrfFilter(String[] patterns) { CsrfFilter csrfFilter = new CsrfFilter(csrfTokenRepository()); csrfFilter.setRequireCsrfProtectionMatcher(csrfProtectionMatcher(patterns)); return csrfFilter; } private NoAntPathRequestMatcher csrfProtectionMatcher(String[] patterns) { return new NoAntPathRequestMatcher(patterns); } private CsrfTokenRepository csrfTokenRepository() { HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); repository.setHeaderName(X_XSRF_TOKEN); return repository; } /** * bCryptPasswordEncoder * * @return */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * configureGlobal * * @param auth * @throws Exception */ @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser(user).password(password).roles("USER"); } /** * sessionRepository * * @return */ @Bean @SuppressWarnings("rawtypes") public SessionRepository sessionRepository() { return new MapSessionRepository(); } /** * sessionStrategy * * @return */ @Bean public HeaderHttpSessionStrategy sessionStrategy() { return new HeaderHttpSessionStrategy(); } /** * @return the user */ public String getUser() { return user; } /** * @param user * the user to set */ public void setUser(String user) { this.user = user; } /** * @return the password */ public String getPassword() { return password; } /** * @param password * the password to set */ public void setPassword(String password) { this.password = password; } } //////////////////////////////////////////////////////////////////////////////////////////// public class CsrfGrantingFilter implements Filter { public static final String X_XSRF_TOKEN = "X-XSRF-TOKEN"; private String loginPage; private final Logger log = LoggerFactory.getLogger(this.getClass()); /** * Login Page injection * @param loginPage */ public CsrfGrantingFilter(String loginPage) { this.loginPage = loginPage; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { CsrfToken csrf = (CsrfToken) servletRequest.getAttribute(CsrfToken.class.getName()); String token = csrf.getToken(); if (token != null && isAuthenticating(servletRequest)) { servletRequest.setAttribute(X_XSRF_TOKEN, true); } else { servletRequest.setAttribute(X_XSRF_TOKEN, false); } filterChain.doFilter(servletRequest, servletResponse); } private boolean isAuthenticating(ServletRequest servletRequest) { HttpServletRequest request = (HttpServletRequest) servletRequest; return request.getRequestURI().equals(loginPage); } @Override public void destroy() { LogUtil.info(log, this.toString()); } @Override public void init(FilterConfig filterConfig) throws ServletException { LogUtil.info(log, filterConfig.toString()); } } /////////////////////////////////////////////////////////////////////////////// @RequestMapping(method = POST, path = "", produces = APPLICATION_JSON_VALUE) public Maplogin(@RequestBody UserEntry userEntry, ServletRequest servletRequest, ServletResponse servletResponse) { boolean xsrfAvailable = (boolean) servletRequest.getAttribute(X_XSRF_TOKEN); Authorization aut = client.login(userEntry); Map resolve = new HashMap (); if (aut != null && aut.isAccepted() && xsrfAvailable) { CsrfToken csrf = (CsrfToken) servletRequest.getAttribute(CsrfToken.class.getName()); String token = csrf.getToken(); HttpServletResponse response = (HttpServletResponse) servletResponse; Cookie cookie = new Cookie(X_XSRF_TOKEN, token); cookie.setPath("/"); cookie.setSecure(true); response.addCookie(cookie); resolve.put(STATUS, AUTHORIZED); resolve.put(USERNAME, username); resolve.put(PASSWORD, password); resolve.put(ROLE, aut.getRole()); resolve.put(TOKEN, token); } else { resolve.put(STATUS, UNAUTHORIZED); } return resolve; }
Básicamente lo que sucede en este ejemplo es lo siguiente:
- El punto de control que genera el Cross Site Request Forgery es /login
- Si el login no es efectivo el CSRF no aparece como set en el cookie
- Ahora bien el toque esta en que el body también presenta el basic autentification y el CSRF. De esta forma es mas sencillo que el Javascript almacene los datos.
- Cada nuevo request debe enviar los Basic Login y el CSRF.
- La aplicacion fija el toking en el login.
- Si cualquier otro request que no este en la lista de permitidos es invocada. Revisa el Basic Aut y el CSRF toking. La clase que genera los toking es CsrfGrantingFilter.