Spring Boot Security PermitAll with RequestHeaderMatcher not working - java

After having followed a tutorial about microserves and OAuth2 in Maven Spring Boot, I got a problem. I want to exclude a request from the authentication, so unauthorized data can be gotten. This only doesn't seem to work in the way I do it. Can someone help me with this?
Tutorial I followed: https://developer.okta.com/blog/2018/02/13/secure-spring-microservices-with-oauth#microservices-architectures-with-spring-boot--spring-cloud
What I tried:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
.authorizeRequests()
.antMatchers("/**").authenticated()
.and()
.authorizeRequests()
.andMatchers(HttpMethod.GET, "/beers").permitAll();
}
}
I have to authenticate when I try to do the request. How do I solve this?
spring-security-oauth2-autoconfigure: 2.1.1.RELEASE

Firstly, your configuration is the same as the followings . Just removing those unnecessary duplicated authorizeRequests() and and() , which make it look more clearly :
http.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
.authorizeRequests()
.antMatchers("/**").authenticated()
.andMatchers(HttpMethod.GET, "/beers").permitAll();
It means spring security will only handle the requests if it has Authorization header. Otherwise ,the request will be ignored and no spring security stuff will apply to it.
So if the request has the Authorization header , it will then start to check the rules (i.e. those matcher things configured by authorizeRequests()) from the top to the bottom according to the declaration order.Once a rule is matched , the bottom rule will be ignored and will not be checked.
Since your first rule is to match every HTTP request ("/**") which makes all rules below it never execute and does not have any meaning.
On the other hand , if you want spring security totally ignore "/beers" even its request has Authorization header , you should configure WebSecurity to ignore it :
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.GET, "/beers");
}

Related

Spring Boot OAuth2 SSO not working when authorizeRequests antMatcher is not set to root url

I've been trying to make an application with Spring Boot that uses Google as an identity provider for logging in.
The following configuration (using Spring Security) seems to work for this:
#Configuration
#EnableOAuth2Sso
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**","/callback/", "/error**")
.permitAll()
.anyRequest()
.authenticated();
}
}
However, I only really want to secure a small part of the website (e.g. everything under the /secured/ path).
So I thought changing the antMatcher that requests are authorized for would work for this:
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
antMatcher("/secured**")
.authorizeRequests()
...
When I try this the application still redirects me to /login, but it gives me a 404 on that page instead of redirecting me to google's servers.
I could of course add all public urls to the permitAll antMatcher, but that seems cumbersome to do everytime a new one is added.
Putting all public things on a /public** path is also not something I'd like to do since it'd look weird in the url
Could anybody shed some light on what is happening here or maybe offer alternative solutions?
Thanks in advance!

Spring Boot 2: Basic Http Auth causes unprotected endpoints to respond with 401 "Unauthorized" if Authorization header is attached

After migration to Spring Boot 2 and adding basic authorization requirement for actuator and another application controlling endpoint it became impossible to call any unprotected endpoint with Authorization header.
Configuration snippet:
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.requestMatchers(EndpointRequest.to("shutdown")).fullyAuthenticated()
.antMatchers("/payment/status/*").fullyAuthenticated()
.and().httpBasic();
}
E.g. call to .../health with "Authorization: Basic ..." will cause 401 "Unauthorized" even though it is not protected by spring security.
Question: How can i adjust the configuration so that it is possible to send request with Authorization header to any unprotected endpoint without being denied?
UPD: This fix worked as i wanted
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.requestMatchers(EndpointRequest.to("shutdown")).fullyAuthenticated()
.antMatchers("/payment/status/*").fullyAuthenticated()
.antMatchers("/payment/**").permitAll()
.and().httpBasic();
}
UPD2: Nevermind, just tested another request and still receive 401 "Unauthorized".
curl localhost:8080/payment/<any_endpoint> -H "Authorization: Basic asdadas"
{"code":401,"message":"Unauthorized"}
This approach unfortunately overrides HttpSecurity matchers, e.g.: /payment/ becomes accessible
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.requestMatchers(EndpointRequest.to("shutdown")).fullyAuthenticated()
.antMatchers("/payment/status/*").fullyAuthenticated()
.and().httpBasic();
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/payment/**");
}
UPD 3: I've created a simple project with this issue being reproduced
https://github.com/Anoobizzz/SpringSecurity2BasicAuthDemo
/internal & /shutdown are only accessible with user:P455W0RD
/exposed accessible without authorization
/exposed with header "Authorization: Basic 123123" responds with 401 "Unauthorized"
By calling .authorizeRequests() , you enforce authorization of all these requests because you've not called .ignore() on some matcher.
I suggest to use ignore on a ** matcher and then incrementally enforce authorization on specified matchers ontop of the permit-all layer so that everything is accessible except of the ones explicitly specified.
This accomplishes what you want to do but beware, it's not a best practise for a very good reason: You should deny all unauthorized traffic by default and only explicitly permit unauthorized requests for specific route templates.
That said, it would be wiser to just use ignore explicitly on the routes you want to be accessible without authentication, not just ** (for example only for /home - /about - /login - /signup)
Found a solution un cased if case when using the spring security oauth2 library. For me it went wrong in the org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter. The default behaviour this filter extracts all Authorization when send.
I solved this by reconfiguring this one;
OAuth2AuthenticationProcessingFilter.setTokenExtractor(myTokenExtractor)
My token extractor I have implemented like this
public class MyTokenExtractor implements TokenExtractor {
private final BearerTokenExtractor bearerTokenExtractor = new BearerTokenExtractor()
#Overide
public Authentication extract(HttpServletRequest request) {
String requestURI = request.getRequestURI();
if (requestURI.startsWith("/someuri") {
return null;
}
return bearerTokenExtractor.extract(request);
}

Spring Boot HttpSecurity fluent api order?

Spring 2.0.3.RELEASE
Goal: Implement Spring Boot Security (basic auth for starters) on all endpoints except /actuator/health, /actuator/info and /ping (a custom controller that just returns ResponseEntity.status(HttpStatus.NO_CONTENT).build()).
The below gives me a 401. Any combination seems to either give me complete anonymous access to all endpoints or 401 to all.
I've set the spring.security.user.name and ...password in application.yml and it is working correctly.
I've implemented...
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
super.configure(http);
// just trying to get health working for starters
http.authorizeRequests().antMatchers("/actuator/health").permitAll()
.anyRequest().authenticated()
.and().formLogin().permitAll();
}
}
The below seemed like it was restricted to Actuator's /health and /info endpoints, but instead is also opening up my custom /ping endpoint as well (it's not in this list).
http.requestMatcher(EndpointRequest.to("health", "info"))
.authorizeRequests().anyRequest().permitAll();
The issue ended up being a bug in Spring Tool Suite. Using Boot Dashboard with a Gradle project wasn't always picking up build output. It seems to be using a different directory and I cannot figure it out.
The HttpSecurity configuration that ended up working for me was:
#Override
protected void configure(final HttpSecurity http) throws Exception {
// #formatter:off
http.
authorizeRequests().
antMatchers("/ping", "/actuator/health", "/actuator/info", "/login").permitAll().
anyRequest().authenticated().and().
httpBasic().and().
// CSRF tokens for API access
csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// #formatter:on
}

Filter ordering with spring security and spring boot

I'm trying to use spring security to secure a rest/stateless api using JWT tokens. From the research I've been seeing, it involves turning off the spring security session management and then adding some custom filters to handle the user logging in as well as checking for the jwt token.
The problem I'm having is that once i add a filter, it's run on every instead of just the endpoints I want it on. I need to open up the login endpoint as well as a few others that will facilitate enrollment and reference data that doesn't need to be secured.
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/user").permitAll()
.anyRequest().authenticated().and()
.addFilterBefore(new StatelessAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
;
}
}
All StatelessAuthenticationFilter does is print "in here". I'm only expecting to see that message print when you go to localhost:8080/api/order, but i see it show up when you go to localhost:8080/api/user.
Is there a way to get this behavior?
The way you configured, the HttpSecurity will be applied to all the URLs including the user endpoint.
authorizeRequests() .antMatchers("/api/user").permitAll() line won't prevent "user" endpoint from authentication filter being called.
It just says that any authenticated user can call it.
You need to apply the filter to "order" endpoint only. Like this:
http .requestMatchers().antMatchers("/api/user") .and() .authorizeRequests().
#tsolakp's answer sorta works for me. I ended up overriding the
configure(Websecurity) method though
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/user");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated().and()
.addFilterBefore(new StatelessAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
;
}

Java Config for Spring Security with Vaadin

Im new to these frameworks (Vaadin:7.6.1, Spring Security:4.0.3) and I'm asking myself how to configure the authorized requests if I want to build a Vaadin application.
I looked up a few examples where something like this is written:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
[...]
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/UIDL/**").permitAll()
.antMatchers("/HEARTBEAT/**").authenticated()
.antMatchers("/VAADIN/**").permitAll()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll()
.and()
.csrf().disable();
}
}
Because I want to design the login page I use the Thymeleaf engine. Therefore I'm using this Controller class:
#Controller
public class LoginController
{
#RequestMapping("/login")
String login(Model model)
{
return "login";
}
}
Which .antMatchers() should I define if I want to block every request of my application if the user isn't logged in? I know that I have to define antMatchers("/resources/**").permitAll() for the login page to get the css and images. But what are these patterns like "/UIDL/**" and what do I need them for?
Which .antMatchers() should I define if I want to block every request
of my application if the user isn't logged in?
If you just want to block every request if the user isn't logged in:
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout().permitAll()
.and()
.csrf().disable();
}
You don't really need any antMatcher, not even for the login page, as in the .formLogin() part, you already include .permitAll() for that page.
Now for static resources (css, js, images) and with VAADIN in mind, you can do this overriding another method:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/resources/**", "/VAADIN/**");
}
With a Spring Boot project, i also found issues if i didn't allow requests to "/vaadinServlet/**"in the web.ignoring().antMatchers(...).
what are these patterns like "/UIDL/**" and what do I need them for?
When the server receives a request, Spring Security uses these patterns to determine if it should allow or deny access to the request.
They represent the part of the URI after the context root of your application, e.g. in the case of your context root being /, then a request like http://server.com/UIDL/hello the part of the URI that Spring Security will use to determine wether to give acces or not will be /UIDL/hello
The ** represents anything including any sub level, e.g. for the /UIDL/** pattern, the request /UIDL/hello/world/and/any/more/levels will match.
There's also the single * which represents, anything but not including the sub levels, e.g. for the /UIDL/* pattern, the request /UIDL/hello will match, but not /UIDL/hello/world.
As for VAADIN views and UIs, i'm not sure that it is possible to use the antMatchers to grant or deny access, but instead you can annotate the configuration class with #EnableGlobalMethodSecurity(prePost = enabled) and then be able to use the #PreAuthorize( /* spel expression */) annotation on the views to grant or deny access.
UPDATE : Answering to comment questions:
Why do you use the configure(WebSecurity web) method with ignoring the resources instead of the configure(HttpSecurity http) with allowing access? Are there significant differences?
The difference is that WebSecurity#ignoring() makes the request being skipped from the Spring Security filter chain, and it is the recommended way for static resources, anything else than static resources should be processed inside configure(HttpSecurity http).
source
Why do you ignore the "/VAADIN/**" path?
Because that path is used to serve themes, widget sets, and customizations, which is static content, the path is used to serve it dinamycally from the Vaadin jar, but as suggested in the Vaadin documentation, in production environments should be served statically, as it is faster.
source
I could imagine the meaning of "/*" and "/**", but what does "UIDL" and "HEARTBEAT" actually mean? Why are they permitted?
UIDL:
User Interface Definition Language (UIDL) is a language for
serializing user interface contents and changes in responses from web
server to a browser. The idea is that the server-side components
"paint" themselves to the screen (a web page) with the language. The
UIDL messages are parsed in the browser and translated to GWT widgets.
source
Heartbeat requests are performed periodically to verify that the connection is still alive between server and client, or the session haven't expired.
source - see sections 4.8.5, 4.8.6, 4.8.7 and 4.8.8

Categories

Resources