Secure specific endpoint by role - spring-boot

I'm trying to build REST API with Spring Boot, secured by Spring Security. Here I need to provide /users endpoint which will be available only to users with ADMIN role.
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
new TestingAuthenticationToken("username", "password", "ROLE_ADMIN");
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
http.authorizeRequests()
.antMatchers("/users").hasRole("ADMIN")
.antMatchers("/products").permitAll()
;
}
}
I'm using TestingAuthenticationToken with ROLE_ADMIN, so I expect that /users endpoint will be available in this configuration.
Request:
GET /users HTTP/1.1
Host: localhost:5000
Accept: application/json
Content-Type: application/json
Cache-Control: no-cache
Response:
"timestamp": "2020-09-01T17:28:27.628+00:00",
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/users"
}

The SecurityContext with its Authentication is retrieved during each request in the SecurityContextPersistenceFilter. Your SecurityContext in the SecurityConfig is hence simply overwritten (with auth == null). No authentication means no role hence 403 - forbidden.
For more see here.

Related

Allow only acces by Zuul

I have several microservices, that I only want to access them by zuul resources server and avoid direct access.
Zuul server implements jwt authentication.
I have these redirections in application.yaml file
zuul:
routes:
servicio-carga-electores:
path: /cargaElectores/**
service-id: servicio-carga-electores
url: ${server-uri}/cargaElectoresWS
servicio-oauth-server:
service-id: servicio-oauth-server
path: /oauth/**
url: ${server-uri}/celec-oauth-server
sensitive-headers:
- Cookie
- Set-Cookie
ribbon:
eureka:
enabled: false
Trying with postman, after getting jwt token, all redirections causes 403 forbidden.
http://localhost:8080/celec-zuul-server/cargaElectores/recupera_ficheros
{
"timestamp": "2022-08-24T08:00:12.806+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/cargaElectoresWS/recupera_ficheros"
}
I've seen in other places using hasIpAdress, but isn't working.
#Configuration
#EnableWebSecurity
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/estado.jsp").permitAll()
.and()
.authorizeRequests()
.antMatchers("/**").hasIpAddress("::1");
}
}
If I don't use oauth2, the redirection goes right, but I can access directly to the microservices.
Thanks for advance.
José Pascual

Spring WS not accepting basic auth credentials

I've been struggling with this one for a while and can't figure out what I'm doing wrong.
I'm supposed to add basic authentication to my SOAP web service in Spring. I made the security config pretty simple (maybe too simple) so it concentrates on basic auth only (see below).
When I'm accessing the base URL from the browser, the authentication seems to be working, it asks for the credentials and if I provide them correctly, it accepts them.
However, when I want to send the SOAP request that contains the basic auth header to my web service endpoint, Spring Security sends back 401 to me. I tried sending the request with SOAPUI, Postman and from Windows Powershell via Invoke-WebRequest, and the result is the same whereas if I catch the request with Wireshark, the right header is there.
I'm using Spring Boot 2.1.8 for this project (same version with Spring Web Services and Security).
The security config class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("foo")
.password(passwordEncoder().encode("bar"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and().httpBasic();
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
To my understanding, there is nothing specific I have to add to the web service config itself so all related basic auth settings can be done in the security config class. Or am I wrong?
Appreciate your help.
Update
Here is the request/response pair:
REQUEST:
POST /foo/endpoint/ HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "http://foo.bar"
Authorization: Basic Zm9vOmJhcg==
Content-Length: 9688
Host: localhost:1502
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:foo="http://foobar.com/">
<soapenv:Header/>
<soapenv:Body>
// body omitted
</soapenv:Body>
</soapenv:Envelope>
RESPONSE
HTTP/1.1 401
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
WWW-Authenticate: Basic realm="Realm"
Content-Length: 0
Date: Thu, 13 Aug 2020 14:22:29 GMT
configure(HttpSecurity http) needs some modifications to enable the http basic authentication in your code.
authorizeRequests() is used for authorization purposes. Once after a user successfully logged in, what all the resources(endpoints) should be accessible to logged-in user, is defined by authorizeRequests(). I also recommend, while authorizing the endpoints, you better use the antMatchers. For Example: I have modified the above code as
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("foo")
.password(passwordEncoder().encode("bar"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.authorizeRequests()
.antMatchers("/simple/**").hasRole("USER");
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
I hope this would work for you. Try & let me know.
NOTE: In Spring boot, just by adding the spring-boot-starter-security dependency, security gets enabled, without any configurations. So now, you trying to re-configure the basic auto-configuration. By extending, WebSecurityConfigurerAdapter class. That's correct.
POSTMAN: use authorization header

How to have unauthenticated endpoints with Spring Boot Resource Server

I have a spring boot application annotated with #ResourceServer. I am trying to have unauthenticated endpoints as well as authenticated endpoints in my application, however all the endpoints return the same 401 response.
My SecurityConfig looks like this:
#Configuration
#EnableWebSecurity
class SecurityConfig: WebSecurityConfigurerAdapter() {
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/search").permitAll()
.anyRequest().authenticated()
.and().httpBasic()
}
}
But when I send a POST request to /api/search I get this response:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
I also tried having only anyRequest().permitAll() in my SecurityConfig, but it still returns the same response.
It was a pretty noobie mistake. I was actually configuring a WebSecurityConfigurerAdapter instead of a ResourceServerConfigurerAdapter.

Spring Security : Falls back to formLogin on authentication error

I tried to implement a SecurityConfig similar to https://stackoverflow.com/a/33608459 and https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#multiple-httpsecurity
I want my API (/rest/**) to be secured by HttpBasic, and other requests via FormLogin.
This works well... as long as I provide the correct credentials to HttpBasic.
If I provide correct credentials - it response with normal answer.
If I provide no credentials - it responds with a 401 Unauthorized - perfect!
If I provide wrong credentials - it responds with a 302 Found with Location: /login
The last part is what I don't want - I also want a 401 Unauthorized on wrong credentials.
Example Requests:
http http://localhost:8081/rest/
HTTP/1.1 401 WWW-Authenticate: Basic realm="My Realm"
http -a correct:password http://localhost:8081/rest/some/api/
HTTP/1.1 200
http -a wrong:password http://localhost:8081/rest/some/api/
HTTP/1.1 302 Location: http://hive.local:8081/login WWW-Authenticate: Basic realm="My Realm"
Java configuration:
#Configuration
#Order(1)
public static class RestSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthorizationProperties properties;
#Override protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.antMatcher("/rest/**")
.authorizeRequests()
.anyRequest().hasRole("API").and()
.httpBasic()
.realmName(properties.getRealm()).and()
.formLogin().disable()
.csrf().disable();
// #formatter:on
}
}
#Configuration
#Order(2)
public static class FrontendSecurityConfig extends WebSecurityConfigurerAdapter {
#Override public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/app/**", "/webjars/**", "/static/**", "/js/**");
}
#Override protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.anyRequest().hasAnyRole("USER").and()
.formLogin();
// #formatter:on
}
}
I was able to bring some light into this.
The redirect to form login after a failed basic auth request is cause by the dispatcher servlet trying to redirect to the URL /error after failing to validate the credentials.
To get the appropriate error response you need to add /error to the antMatchers that are ignored in your web security config.

Spring Boot Custom Authorization Header request not going through at all

So I have a Spring Boot application and I am sending a request to it using PostMan. It is using Spring Security along with JWT for authentication. I'm trying to get authorization to work but am running into issues. Spring is able to login the user and return a token fine. But when I put the token in the header it's not working at all. I get no response from the server. When the token is removed, it works fine. Right now all requests should be able to go through regardless of being logged in or not.
My Spring Web Configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
}
The REST path I'm trying to access:
#RestController("threadService")
#RequestMapping("/api/thread")
public class ThreadService {
#RequestMapping(value="/list", method=RequestMethod.GET)
public List<ThreadDetails> getThreadList() {
logger.info("getThreadList");
return threadDao.getThreadList();
}
}
The failed GET request I'm issuing after I have logged in and gotten a token:
GET /api/thread/list HTTP/1.1
Host: localhost:8080
Authorization : Bearer (JWT token here)
Cache-Control: no-cache
Postman-Token: 69565839-4806-b4f6-9a03-11382a80c7da
The above request works fine when there is no Authorization in the header.
Not sure it is exactly the problem I was facing.
When I want to communicate with the restservice exposed by spring boot application, the "Authorization" is not set. I followed the steps which are required to communicate but the value wont be passes through header.
The solution I found, the "common-codec" library was missing. Once I add the dependency in my web application, it start sending the "Authorization" in header to my spring boot application.
Hope this helps to someone.

Resources