AntMatcher seems not to match requested path (Spring Security) - spring

We are trying to use Spring Security to secure our webservice. We are using a customer filter (a class that extends GenericFilterBean) to read a JSON Web Token from the HTTP header. If a token exists, it is stored as PreAuthenticatedAuthenticationToken in the Security Context. Protected resources should use our customer Authentication Provider to verify if the token is valid and to load user info (which includes roles).
The problem is that I don't get it to configure an AntMatcher for a specific resource.
If I use antMatchers("/**").hasRole("USER") the resource is protected, but we don't want all resources to match, so I tried an AntMachter for one resource like this:
.antMatchers(HttpMethod.GET,"/rest/security/v1/currentuser").hasRole("USER")
But this matcher seems not to match the requested resource and so the Authentication Provider is not called. But I don't know why. I tried several combinations of ant pattern but nothing worked yet.
I set a breakpoint in the custom filter to check the current path and when I call servletRequest.getPathInfo() I get excatly what I thought it should be our ant pattern: /rest/security/v1/currentuser
Spring Security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.csrf().disable()
.authorizeRequests()
//.antMatchers("/**").hasRole("USER")
.antMatchers(HttpMethod.GET, "/rest/security/v1/currentuser").hasRole("USER")
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.addFilterBefore(new JwtAuthenticationFilter(), BasicAuthenticationFilter.class);
}
Custom filter:
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
Optional<String> token = Optional.ofNullable(httpRequest.getHeader(HTTP_AUTHENTICATION_HEADER));
if (token.isPresent()) {
PreAuthenticatedAuthenticationToken requestAuthentication = new PreAuthenticatedAuthenticationToken(token, null);
SecurityContextHolder.getContext().setAuthentication(requestAuthentication);
}
filterChain.doFilter(servletRequest, servletResponse);
}
Authentication provider:
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken authenticationToken = null;
try {
// Token Validation, Loading User Info
} catch (Exception e) {
LOG.error("Failed to authenticate", e);
}
return authenticationToken;
}

Related

Apply dynamic ACL with spring security and JWT

I have implemented authentication using SpringSecurity, JWT and OAuth2.
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.POST,"/v1/register").permitAll()
.antMatchers(HttpMethod.GET,"/v1/public").permitAll();
List<String> permisos = roleService.findPermisos();
for(String name: permisos) {
String[] data = name.split(",");
http.authorizeRequests().antMatchers(data[0], data[1]).hasRole(data[2]);
}
http.authorizeRequests()
.anyRequest().authenticated()
.and().cors().configurationSource(corsConfigurationSource());
}
Currently the configure(HttpSecurity http) method works on the OAuth2 side. In my database I have all the #RestController to which they are allowed or denied access for each Role, the problem is, it is applied only once the app is compiled and I need is for it to be applied when I modify the allowed or denied access without the need to recompile a dynamic acl shown in List<String> permisos = roleService.findPermisos().
Searching I read that I can use Filters with HttpSecurity, I have not been able to find examples, your help please.
Doing some research I modified my solution as follows:
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.PUT,"/v1/menu").permitAll()
.anyRequest().authenticated().and()
.addFilterAfter(new CustomFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.cors().configurationSource(corsConfigurationSource());
}
I add a Filter addFilterAfter(new CustomFilter(), AbstractPreAuthenticatedProcessingFilter.class) and in CustomFilter() is:
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
ServletContext servletContext = request.getServletContext();
if(usuarioService == null){
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
usuarioService = webApplicationContext.getBean(IUsuarioService.class);
}
String[] path = request.getRequestURI().split("/");
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Usuario usuario = usuarioService.findByEmail(authentication.getName());
if(usuarioService.existsPermission(request.getMethod(), path[2], authentication.getName())) {
filterChain.doFilter(request, response);
} else {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
}
So I changed my solution from a loop to a query through a filter, the problem is that all the Rest must be registered and validated, this would be a problem to allow access to resources that I do not want to be verified.
Please, if you have a better solution, I would really appreciate your help.

Spring Security: oauth2Login redirect only on certain paths

I have Spring Security configured to authenticate my website, such that all paths are automatically redirected to the OAuth2 authorization URL (using .oauth2Login()). However, I want unauthenticated requests to the API (i.e. /api/**) to return 401 Unauthorized instead of being redirected. I can't figure out how to do this. Any help would be much appreciated.
Here is my current configuration:
http
.authorizeRequests()
.antMatchers("/api/auth/oauth2/callback").permitAll()
.anyRequest().authenticated()
.oauth2Login()
.authorizationEndpoint()
.baseUri(this.oauth2AuthorizationRedirectBaseUri);
http.logout()
.logoutUrl("/auth/logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
You can define a custom authentication entry point for /API/** and add t to your configuration:
#Component
public class CustomAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
#Override
public void commence(
HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
#Override
public void afterPropertiesSet() throws Exception {
setRealmName("developers");
super.afterPropertiesSet();
}
}
in your Http security configs add:
http.
...
.exceptionHandling()
.defaultAuthenticationEntryPointFor(
new CustomAuthenticationEntryPoint(),
new AntPathRequestMatcher("/api/**"))

Use AbstractAuthenticationProcessingFilter for multiple URLs

I have the below endpoint patterns in my application
/token -- accessible to all
/rest/securedone/** -- requires authentication
/rest/securedtwo/** -- requires authentication
/rest/unsecured/** -- does not require authentication
As of now, I am able to access the /token endpoint.
But /rest/securedone/** and /rest/unsecured/** return 401 when a token(JWT) is not sent. It is my intention to secure /rest/securedone/** and that is fine /rest/unsecured/** should be accessible.
My httpSecurity config is as below:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/token").permitAll()
.antMatchers("/rest/secured/**").authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.headers().cacheControl();
}
and my AbstractAuthenticationProcessingFilter extended class is as below:
public class MyAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {
private static Logger log = LoggerFactory.getLogger(MyAuthenticationTokenFilter.class);
public MyAuthenticationTokenFilter() { super("/rest/**"); }
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, ServletException {
//authentication handling code
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
chain.doFilter(request, response);
}
}
Can someone please help my figure out the below:
When is the MyAuthenticationTokenFilter used? For which URL will it be invoked? How come, /rest/unsecured/** is also expecting authentication? It happens even if i explicitly say .antMatchers("/rest/secured/**").permitAll().
Can I specify multiple url patterns in my super(defaultFilterProcessingUrl) call inside MyAuthenticationTokenFilter constructor? For example, if I have another url such as /api/secured/**, how can I get my MyAuthenticationTokenFilter to be invoked for /api/secured/** requests? I do not need different authentication handling so I want to re-use this filter.
When is the MyAuthenticationTokenFilter used ?
This filter is using for processing the request with client credential,it will filter the url when the
RequestMatcher match the request url, for example, in your configuration, it will handle the url that matches /rest/**, and try to convert the client credential to Authentication(e.g userInfo, role ...), it maybe throws an exception when the request with incorrect client credential.
It is different to authorizeRequests(xxx.authenticated() or xxx.permit()), authorizeRequests just check the whether the authentication has some special attributes (e.g role, scope).
By way of analogy, AbstractAuthenticationProcessingFilter just puts some cards(Authentication) into a box(SecurityContext) by different clients, authorizeRequests just check the box has the card that it needed, or it will deny the request. AbstractAuthenticationProcessingFilter
don't care who/how to use the cards, and authorizeRequests don't care where the cards come from.
Can I specify multiple url patterns in my super(defaultFilterProcessingUrl) call inside MyAuthenticationTokenFilter constructor ?
Yes, you can set the requiresAuthenticationRequestMatcher by setRequiresAuthenticationRequestMatcher, it will override the old requiresAuthenticationRequestMatcher, for example,
authenticationTokenFilter
.setRequiresAuthenticationRequestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/rest/secured/**")
, new AntPathRequestMatcher("/api/secured/**")
));

Spring Security Filter is not being triggered by configuration

I am configuring the security of my REST and I don't know how I can secure my methods, however allowing a filter to trigger to set my Authority
http
.authorizeRequests()
.antMatchers(PERSISTENCE_SERVICE_URL)
.hasAuthority(AUTHORITY_PERSISTENCE_SERVICE)
.and()
.csrf()
.disable();
And in my Filter which extends OncePerRequestFilter does something like this
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
this.authenticationImpl.init();
String jwt = request.getHeader("jwt");
String refresh = request.getHeader("refresh");
if(jwt != null) {
this.jwtPropertyExtractor.commitJwt(jwt, refresh);
String jwtId = this.jwtPropertyExtractor.getIdentityId();
String securityRole = this.jwtPropertyExtractor.getSecurityRole();
this.authenticationImpl.setIdentityId(jwtId);
this.authenticationImpl.updateSecurityRole(securityRole);
SecurityContextHolder.getContext().setAuthentication(this.authenticationImpl);
}
filterChain.doFilter(request, response); }
So when I place .hasAuthority(AUTHORITY_PERSISTENCE_SERVICE) in my configuration, my filter is not even being triggered but i need him to set my authentication.
Ok i added
.addFilterBefore(jwtAuthorisationFilter(),
BasicAuthenticationFilter.class);
}
#Bean
public JwtAuthorisationFilter jwtAuthorisationFilter() {
return new JwtAuthorisationFilter(jwtExtractor(),
authenticationImpl());
}
Now its working fine thx for that hint :>

Spring Security - Token based API auth & user/password authentication

I am trying to create a webapp that will primarily provide a REST API using Spring, and am trying to configure the security side.
I am trying to implement this kind of pattern: https://developers.google.com/accounts/docs/MobileApps (Google have totally changed that page, so no longer makes sense - see the page I was referring to here: http://web.archive.org/web/20130822184827/https://developers.google.com/accounts/docs/MobileApps)
Here is what I need to accompish:
Web app has simple sign-in/sign-up forms that work with normal spring user/password authentication (have done this type of thing before with dao/authenticationmanager/userdetailsservice etc)
REST api endpoints that are stateless sessions and every request authenticated based ona token provided with the request
(e.g. user logins/signs up using normal forms, webapp provides secure cookie with token that can then be used in following API requests)
I had a normal authentication setup as below:
#Override protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/mobile/app/sign-up").permitAll()
.antMatchers("/v1/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.loginProcessingUrl("/loginprocess")
.failureUrl("/?loginFailure=true")
.permitAll();
}
I was thinking of adding a pre-auth filter, that checks for the token in the request and then sets the security context (would that mean that the normal following authentication would be skipped?), however, beyond the normal user/password I have not done too much with token based security, but based on some other examples I came up with the following:
Security Config:
#Override protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.addFilter(restAuthenticationFilter())
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint()).and()
.antMatcher("/v1/**")
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/mobile/app/sign-up").permitAll()
.antMatchers("/v1/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.loginProcessingUrl("/loginprocess")
.failureUrl("/?loginFailure=true")
.permitAll();
}
My custom rest filter:
public class RestAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public RestAuthenticationFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
private final String HEADER_SECURITY_TOKEN = "X-Token";
private String token = "";
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
this.token = request.getHeader(HEADER_SECURITY_TOKEN);
//If we have already applied this filter - not sure how that would happen? - then just continue chain
if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
}
//Now mark request as completing this filter
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
//Attempt to authenticate
Authentication authResult;
authResult = attemptAuthentication(request, response);
if (authResult == null) {
unsuccessfulAuthentication(request, response, new LockedException("Forbidden"));
} else {
successfulAuthentication(request, response, chain, authResult);
}
}
/**
* Attempt to authenticate request - basically just pass over to another method to authenticate request headers
*/
#Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
AbstractAuthenticationToken userAuthenticationToken = authUserByToken();
if(userAuthenticationToken == null) throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
return userAuthenticationToken;
}
/**
* authenticate the user based on token, mobile app secret & user agent
* #return
*/
private AbstractAuthenticationToken authUserByToken() {
AbstractAuthenticationToken authToken = null;
try {
// TODO - just return null - always fail auth just to test spring setup ok
return null;
} catch (Exception e) {
logger.error("Authenticate user by token error: ", e);
}
return authToken;
}
The above actually results in an error on app startup saying: authenticationManager must be specified
Can anyone tell me how best to do this - is a pre_auth filter the best way to do this?
EDIT
I wrote up what I found and how I did it with Spring-security (including the code) implementing a standard token implementation (not OAuth)
Overview of the problem and approach/solution
Implementing the solution with Spring-security
Hope it helps some others..
I believe the error that you mention is just because the AbstractAuthenticationProcessingFilter base class that you are using requires an AuthenticationManager. If you aren't going to use it you can set it to a no-op, or just implement Filter directly. If your Filter can authenticate the request and sets up the SecurityContext then usually the downstream processing will be skipped (it depends on the implementation of the downstream filters, but I don't see anything weird in your app, so they probably all behave that way).
If I were you I might consider putting the API endpoints in a completely separate filter chain (another WebSecurityConfigurerAdapter bean). But that only makes things easier to read, not necessarily crucial.
You might find (as suggested in comments) that you end up reinventing the wheel, but no harm in trying, and you will probably learn more about Spring and Security in the process.
ADDITION: the github approach is quite interesting: users just use the token as a password in basic auth, and the server doesn't need a custom filter (BasicAuthenticationFilter is fine).

Resources