La seguridad con con Springboot es realmente sencilla.
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 Map login(@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.
Dentro de la aplicacion pasa esto:
- 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.