InitBinder to bind validator to some of the arguments - java

I'm using #InitBinder in a controller:
#InitBinder
public void binder(WebDataBinder binder) {
binder.addValidators(new CompoundValidator(new Validator[] {
new UserAccountValidator()}));
}
#Override
public UserAccountEntity login(#RequestBody UserAccountEntity userAccount,
HttpServletResponse response) throws InvalidCredentialsException, InactiveAccountException {
return userAccountService.authenticateUserAndSetResponsenHeader(
account.getUsername(), account.getPassword(), response);
}
#Override
public UserAccountEntity create(#Valid #RequestBody UserAccountEntity userAccount,
HttpServletResponse response) throws EntityExistsException, InvalidCredentialsException, InactiveAccountException {
String username = userAccount.getUsername();
String password = userAccount.getPassword();
userAccountService.saveIfNotExistsOrExpired(username, password);
return userAccountService.authenticateUserAndSetResponsenHeader(
username, password, response);
}
I want the validator to only validate the incoming userAccount for the login endpoint, and not the create method. Right now, it validates on both methods.
Update 1
Code for the CompoundValidator:
public final class CompoundValidator implements Validator {
private final Validator[] validators;
public CompoundValidator(final Validator[] validators) {
super();
this.validators=validators;
}
#Override
public boolean supports(final Class<?> clazz) {
for (Validator v : validators) {
if (v.supports(clazz)) {
return true;
}
}
return false;
}
#Override
public void validate(Object target, Errors errors) {
for (Validator v : validators) {
if (v.supports(target.getClass())) {
v.validate(target, errors);
}
}
}
}
Update 2
Some config files:
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserAccountService userAccountService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// TODO re-enable csrf after dev is done
.csrf()
.disable()
// we must specify ordering for our custom filter, otherwise it
// doesn't work
.addFilterAfter(jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
// we don't need Session, as we are using jwt instead. Sessions
// are harder to scale and manage
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
/*
* Ignores the authentication endpoints (signup and login)
*/
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/authentication/**").and().ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**");
}
/*
* Set user details services and password encoder
*/
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userAccountService).passwordEncoder(
passwordEncoder());
}
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
/*
* By default, spring boot adds custom filters to the filter chain which
* affects all requests this should be disabled.
*/
#Bean
public FilterRegistrationBean<JwtAuthenticationFilter> rolesAuthenticationFilterRegistrationDisable(
JwtAuthenticationFilter filter) {
FilterRegistrationBean<JwtAuthenticationFilter> registration = new FilterRegistrationBean<JwtAuthenticationFilter>(
filter);
registration.setEnabled(false);
return registration;
}
}
--
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/public/")
.resourceChain(false).addResolver(new CustomResourceResolver());
}
private class CustomResourceResolver implements ResourceResolver {
private Resource index = new ClassPathResource("/public/index.html");
private List<String> handledExtensions = Arrays.asList("css", "png",
"svg", "jpg", "jpeg", "gif", "ico", "js");
private List<String> ignoredPaths = Arrays.asList("^api\\/.*$");
#Override
public Resource resolveResource(HttpServletRequest request,
String requestPath, List<? extends Resource> locations,
ResourceResolverChain chain) {
return resolve(requestPath, locations);
}
#Override
public String resolveUrlPath(String resourcePath,
List<? extends Resource> locations, ResourceResolverChain chain) {
Resource resolvedResource = resolve(resourcePath, locations);
if (resolvedResource == null) {
return null;
}
try {
return resolvedResource.getURL().toString();
} catch (IOException e) {
return resolvedResource.getFilename();
}
}
private Resource resolve(String requestPath,
List<? extends Resource> locations) {
if (isIgnored(requestPath)) {
return null;
}
if (isHandled(requestPath)) {
return locations
.stream()
.map(loc -> createRelative(loc, requestPath))
.filter(resource -> resource != null
&& resource.exists()).findFirst()
.orElseGet(null);
}
return index;
}
private Resource createRelative(Resource resource, String relativePath) {
try {
return resource.createRelative(relativePath);
} catch (IOException e) {
return null;
}
}
private boolean isIgnored(String path) {
return !ignoredPaths.stream().noneMatch(
rgx -> Pattern.matches(rgx, path));
}
private boolean isHandled(String path) {
String extension = StringUtils.getFilenameExtension(path);
return handledExtensions.stream().anyMatch(
ext -> ext.equals(extension));
}
}
// TODO remove this after active development of the front-end
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.exposedHeaders("Authorization", "Content-Type")
.allowedMethods("*");
}
}

Related

After Using HttpServletRequestWrapper I'm getting HttpMessageNotReadableException Required request body is missing

I'm trying to make an authorization mechanism in spring boot. Where I'm returning the PreAuthenticatedPrincipal as a body from the request. But after using the wrapper I'm getting a body not found error.
This is the authFilter
public class AuthKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
#SneakyThrows
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
CustomHttpRequestBody wrappedRequest = new CustomHttpRequestBody((HttpServletRequest) request);
return new String(wrappedRequest.getBody());
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}
This is the Custom Wrapper
#Getter
public class CustomHttpRequestBody extends HttpServletRequestWrapper {
private final String body;
public CustomHttpRequestBody(HttpServletRequest request) throws IOException {
super(request);
InputStream requestInputStream = request.getInputStream();
body = new String(StreamUtils.copyToByteArray(requestInputStream));
}
#Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
#Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
#Override
public boolean isReady() {
return true;
}
#Override
public void setReadListener(ReadListener listener) {
// TODO Auto-generated method stub
}
};
return servletInputStream;
}
}
This is the securityconfig
#Configuration
#EnableWebSecurity
#Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthorizationServiceHandler authorizationServiceHandler;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
AuthKeyAuthFilter filter = new AuthKeyAuthFilter();
filter.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (null == principal || !authorizationServiceHandler.verify(principal))
{
throw new BadCredentialsException("Authorization Error, refer to Documentation");
}
authentication.setAuthenticated(true);
return authentication;
}
});
httpSecurity.
antMatcher("/api/**").
csrf().disable().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
and().addFilter(filter).authorizeRequests().anyRequest().authenticated();
}
}
Quick help appreciated,
Thanks

Wrong Authentication-Object in Controller [Spring-Boot]

I'm pretty new to Spring-Boot. I have tried to block certain routes, permit some and implement authentication for them. This works so far, but somehow I want to get the user who makes the request.
My WebSecurityConfig:
#Configuration
#Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtTokenService tokenService;
public WebSecurityConfig(JwtTokenService tokenService) {
this.tokenService = tokenService;
}
#Bean
public JwtTokenFilter tokenFilter() {
return new JwtTokenFilter();
}
#Bean
public SecurityContextHolderAwareRequestFilter securityContextHolderAwareRequestFilter() {
return new SecurityContextHolderAwareRequestFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/login", "/apply").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(tokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.headers().cacheControl();
}
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder builder) {
builder.authenticationProvider(new AuthenticationProvider() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String token = (String) authentication.getCredentials();
return tokenService.decode(token).map(AuthenticatedUser::new).orElseThrow(JwtAuthenticationException::new);
}
#Override
public boolean supports(Class<?> authentication) {
return JwtAuthentication.class.equals(authentication);
}
});
}
}
The TokenFilter:
#Component
public class JwtTokenFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
String header = request.getHeader("Anima-Authentication-Token");
if(header != null) {
SecurityContextHolder.getContext().setAuthentication(new JwtAuthentication(header));
}
filterChain.doFilter(request, response);
}
}
The Authentications:
public class JwtAuthentication implements Authentication {
private final String token;
public JwtAuthentication(String token) {
this.token = token;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
#Override
public Object getCredentials() {
return token;
}
#Override
public Object getDetails() {
return null;
}
#Override
public Object getPrincipal() {
return null;
}
#Override
public boolean isAuthenticated() {
return false;
}
#Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
#Override
public String getName() {
return null;
}
}
public class AuthenticatedUser implements Authentication {
private final AnimaUser user;
public AuthenticatedUser(AnimaUser user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return new ArrayList<>();
}
#Override
public Object getCredentials() {
return null;
}
#Override
public Object getDetails() {
return null;
}
#Override
public Object getPrincipal() {
return user;
}
#Override
public boolean isAuthenticated() {
return true;
}
#Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
#Override
public String getName() {
return user.getName();
}
}
I also tried to override the Authentication in the SecurityContextHolder with the AuthorizedUser Object:
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder builder) {
builder.authenticationProvider(new AuthenticationProvider() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String token = (String) authentication.getCredentials();
return tokenService.decode(token).map(user - > {
SecurityContextHolder.getContext().setAuthentication(new AuthenticatedUser(user));
return new AuthenticatedUser(user);
}).orElseThrow(JwtAuthenticationException::new);
}
#Override
public boolean supports(Class<?> authentication) {
return JwtAuthentication.class.equals(authentication);
}
});
}
But this hasn't worked either. I have tried to access the User with the following methods:
#GetMapping("")
#ResponseBody
public String handle() {
// This returns the JwtAuthentication, not the AuthenticatedUser
return SecurityContextHolder.getContext().getAuthentication().getCredentials().toString();
}
#GetMapping("")
#ResponseBody
public String handle(Authentication authentication) {
// authentication is null
}
#GetMapping("")
#ResponseBody
public String handle(Principle principle) {
// principle is null
}
It is because of #Component annotation on JwtTokenFilter class. Remove that and you will be good to go. You are already defining that as a #Bean in your WebSecurityConfig class. Since you have #Component on the class it is running after the AuthenticationProvider code overriding the AuthenticatedUser set in SecurityContext with JwtAuthentication

Configure WebRequestInterceptor in Spring Configuration

I am trying to use WebRequestInterceptor but i don't know how can i configure it in spring boot, as if I implement WebMvcConfigurer interface it requires a HandlerInterceptor object so i cannot assign my interceptor to it. Any help would be highly appreciated.
Interceptor class:
public class CustomerStateInterceptor implements WebRequestInterceptor {
#Resource(name = "customerStateRequestProcessor")
private CustomerStateRequestProcessor customerStateRequestProcessor;
#Override
public void preHandle(WebRequest webRequest) {
customerStateRequestProcessor.process(webRequest);
}
#Override
public void postHandle(WebRequest webRequest, ModelMap modelMap) {
//unimplemented
}
#Override
public void afterCompletion(WebRequest webRequest, Exception e) {
//unimplemented
}
}
and config class:
#Configuration
public class InterceptorConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomerStateInterceptor()); // <-- Error here.
}
}
You supposed to implement HandlerInterceptor from org.springframework.web.servlet package and not WebRequestInterceptor.
Update
You can just wrap with WebRequestHandlerInterceptorAdapter:
#Configuration
public class InterceptorConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(
new WebRequestHandlerInterceptorAdapter(
new CustomerStateInterceptor()));
}
}
Add filter class to your package and please try the code below -
public class RequestValidateFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
try {
request = new RequestWrapper(httpServletRequest);
chain.doFilter(request, response);
} catch (Exception e) {
throw new ServletException();
}
}
}
FilterClass :
#Configuration
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.addFilterBefore(requestValidateFilter(), BasicAuthenticationFilter.class);
http.authorizeRequests().antMatchers("/projectname/**").authenticated();
http.addFilterAfter(responseValidateFilter(), BasicAuthenticationFilter.class);
}
private RequestValidateFilter requestValidateFilter() {
return new RequestValidateFilter();
}
private ReponseValidateFilter responseValidateFilter() {
return new ReponseValidateFilter();
}
}

Endpoint Response got interchanged in spring boot application

I have a spring boot application i have many endpoint in this application. When i am hitting endpoint simultaneously JSON response from two different endpoint got interchanged.
For example:
i am hitting /currency/list endpoint and /fee endpoint and i am getting /fee endpoint data in currency/list endpoint and vice versa.
I have no idea why this happening. If anybody can suggest why happening will be helpful.
Also I am using spring security token based auth in this project
CurrencyController.java
#RestController
#RequestMapping(value = UrlConstant.BASE_ADMIN_URI_V1)
#Api(value = "Admin Controller")
#Scope("request")
public class CurrencyController {
public static final Logger logger = LoggerFactory.getLogger(CurrencyController.class);
#Autowired
private LocaleService localService;
#RequestMapping(value = UrlConstant.CURRENCY_LIST_FOR_MARKET, method = RequestMethod.GET)
public ResponseEntity<Object> getCurrencyListForMarket() {
List<Currency> currencyList = currencyService.getCurrencyListForMarket();
ObjectMapper mapper = new ObjectMapper();
try {
String stringList = mapper.writeValueAsString(currencyList);
logger.debug("all currency list as String: {}", stringList);
} catch (JsonProcessingException e) {
logger.debug("error in currency list: {}", e.getMessage());
e.printStackTrace();
}
return ResponseHandler.response(HttpStatus.OK, false, localService.getMessage("currency.list.success"),
currencyList);
}
}
AdminController.java
#RestController
#RequestMapping(value = UrlConstant.BASE_ADMIN_URI_V1)
#Api(value = "Admin Controller")
#Scope("request")
public class AdminController {
#Autowired
private LocaleService localeService;
#Autowired
private FeeService feeService;
#RequestMapping(value = UrlConstant.TRADING_FEES, method = RequestMethod.GET)
public ResponseEntity<Object> getTradingFees() {
TradingFee fee = tradingFeeService.getTradingFee();
return ResponseHandler.response(HttpStatus.OK, true,
localeService.getMessage("admin.transaction.fees.found.success"), fee);
}
}
TokenAuthenticationFilter.java
public class TokenAuthenticationFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
// extract token from header
String token = httpRequest.getHeader("Authorization");
if (token != null && !token.isEmpty()) {
AuthenticationTokenRepo authenticationTokenRepository = WebApplicationContextUtils
.getRequiredWebApplicationContext(httpRequest.getServletContext())
.getBean(AuthenticationTokenRepo.class);
// check whether token is valid
AuthenticationToken authToken = authenticationTokenRepository.findByToken(token);
if (authToken != null) {
// Add user to SecurityContextHolder
final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
authToken.getUser(), null, new ApplicationUserDetail(authToken.getUser()).getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
SecurityContextHolder.clearContext();
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment environment;
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers(HttpMethod.POST, "/api/v1/login").permitAll()
.antMatchers(HttpMethod.POST, "/api/v1/user/register").permitAll().anyRequest().authenticated();
// Implementing Token based authentication in this filter
final TokenAuthenticationFilter tokenFilter = new TokenAuthenticationFilter();
http.addFilterBefore(tokenFilter, BasicAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(UrlConstant.BASE_ADMIN_URI_V1 + UrlConstant.CURRENCY_LIST_FOR_MARKET);
web.ignoring().antMatchers(UrlConstant.BASE_ADMIN_URI_V1 + UrlConstant.TRADING_FEES);
}
}
}
ApplicationUserDetail.java
public class ApplicationUserDetail implements UserDetails,Serializable {
private static final long serialVersionUID = 1L;
transient User user;
public ApplicationUserDetail(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole().getName());
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getEmailId();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return user.getIsEnabled();
}
}
More info: When i am printing response in my api it is correct but when i am printing it in my Authentication filter i got the response of fee api in my currency api so i think there is some problem between api to filter.

how to ignore spring security CSRF for specific URL's in spring boot project

how can I ignore CSRF security for specific URL which is like "/workflow/**".
Except for this URL, I need both authorization and CSRF security for all the URL's and methods.
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
private PranaUserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().requireCsrfProtectionMatcher(new AllExceptUrlStartedWith("/workflow"))
.and().authorizeRequests()
.antMatchers("/rest/**", "/tasklist").authenticated()
.and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/index.html")
.and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.and().formLogin().successHandler(authenticationSuccessHandler)
.and().formLogin().failureHandler(authenticationFailureHandler)
.and().csrf().csrfTokenRepository(csrfTokenRepository()).and().addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
}
private static class AllExceptUrlStartedWith implements RequestMatcher {
private static final String[] ALLOWED_METHODS =
new String[] {"GET"};
private final String[] allowedUrls;
public AllExceptUrlStartedWith(String... allowedUrls) {
this.allowedUrls = allowedUrls;
}
#Override
public boolean matches(HttpServletRequest request) {
String method = request.getMethod();
for(String allowedMethod : ALLOWED_METHODS) {
if (allowedMethod.equals(method)) {
return false;
}
}
String uri = request.getRequestURI();
for (String allowedUrl : allowedUrls) {
if (uri.startsWith(allowedUrl)) {
return false;
}
}
return true;
}
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/styles/**").antMatchers("/scripts/**");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
how can I ignore CSRF security for specific URL which is like "/workflow/**".
Except for this URL, I need both authorization and CSRF security for all the URL's and methods.
In my project I'm using the following code:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
...
.csrf()
// Allow unsecured requests to H2 console
.requireCsrfProtectionMatcher(new AllExceptUrlsStartedWith("/console"))
...
}
private static class AllExceptUrlsStartedWith implements RequestMatcher {
private static final String[] ALLOWED_METHODS =
new String[] {"GET", "HEAD", "TRACE", "OPTIONS"};
private final String[] allowedUrls;
public AllExceptUrlsStartedWith(String... allowedUrls) {
this.allowedUrls = allowedUrls;
}
#Override
public boolean matches(HttpServletRequest request) {
// replicate default behavior (see CsrfFilter.DefaultRequiresCsrfMatcher class)
String method = request.getMethod();
for (String allowedMethod : ALLOWED_METHODS) {
if (allowedMethod.equals(method)) {
return false;
}
}
// apply our own exceptions
String uri = request.getRequestURI();
for (String allowedUrl : allowedUrls) {
if (uri.startsWith(allowedUrl)) {
return false;
}
}
return true;
}
}
In this example I've disabled CSRF protection for /console.
Update: since Spring Security 4.0 you can simplify it to a single line:
csrf()
.ignoringAntMatchers("/nocsrf","/ignore/startswith/**")
Only one purpose of answering in this thread is to explain and use antPathMatcher whose advantages can be taken to protect many urls with ant matchers.
From Doc
.csrf().requireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher)
Specify the RequestMatcher to use for determining when CSRF should be applied. The default is to ignore GET, HEAD, TRACE, OPTIONS and process all other requests.
Note that by default GET, HEAD, TRACE, OPTIONS requests are ignored. If you want to override this defaults configure requireCsrfProtectionMatcher(implementation_of_RequestMatcher).
In implementation of RequestMatcher define all the URL's which needs to be protected. You are done
Say you want URL's /api/** to be ensured for CSRF protection.
#Autowired
RequestMatcher csrfProtectedMatchers;
#Override
protected void configure(final HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers("/resources/**", "/", "/login").permitAll()
.antMatchers("/api/**").hasAnyRole("ADMIN", "USER")
.antMatchers("/app/user/*")
.hasAnyRole("ADMIN", "USER")
.and().formLogin()
.and().csrf().requireCsrfProtectionMatcher(csrfProtectedMatchers);
}
#Bean
public RequestMatcher getCsrfProtectedMatchers()
{
UrlPathHelper urlPathHelper = new UrlPathHelper();
AntPathMatcher antPathMatcher = new AntPathMatcher();
List<String> protectedUrlPatterns = Arrays.asList("/api/**", "/logout");
return new RequestMatcher()
{
#Override
public boolean matches(HttpServletRequest request)
{
String uri = urlPathHelper.getPathWithinApplication(request);
for (String pattern : protectedUrlPatterns)
{
if (antPathMatcher.match(pattern, uri))
{
return true;
}
}
return false;
}
};
}
Logic explained
suppose URL: http://localhost:8080/csrf/api/test1
String uri = urlPathHelper.getPathWithinApplication(request);
uri => /api/test1;
antPathMatcher.match("/api/**", "/api/test1") => true
answer for my own question... thanks to #Slava
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
private PranaUserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().requireCsrfProtectionMatcher(new AllExceptUrlStartedWith("/workflow"))
.and().authorizeRequests()
.antMatchers("/rest/**", "/tasklist").authenticated()
.and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/index.html")
.and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.and().formLogin().successHandler(authenticationSuccessHandler)
.and().formLogin().failureHandler(authenticationFailureHandler)
.and().csrf().csrfTokenRepository(csrfTokenRepository()).and().addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
}
private static class AllExceptUrlStartedWith implements RequestMatcher {
private static final String[] ALLOWED_METHODS =
new String[] {"GET"};
private final String[] allowedUrls;
public AllExceptUrlStartedWith(String... allowedUrls) {
this.allowedUrls = allowedUrls;
}
#Override
public boolean matches(HttpServletRequest request) {
String method = request.getMethod();
for(String allowedMethod : ALLOWED_METHODS) {
if (allowedMethod.equals(method)) {
return false;
}
}
String uri = request.getRequestURI();
for (String allowedUrl : allowedUrls) {
if (uri.startsWith(allowedUrl)) {
return false;
}
}
return true;
}
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/styles/**").antMatchers("/scripts/**");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}

Categories

Resources