spring config server encrypt forbidden - spring

I've configured a spring cloud config server to use oAuth2 for security. Everything is working well, except the encrypt end point. When I try to access /encrypt I get a 403 Forbidden. I am including the Authorization Bearer token in the header. Is there a way to allow the encrypt end point to be called when the server is secured with oAuth, or is it always blocked? Let me know if you would like to see any config files for this server.
Just for reference, here are the things that are working.
calling /encrypt/status produces {"status":"OK"}
The git repository is being pulled because I can access a property file from the server.
oAuth authentication is working with Google because it takes me through the logon process.
Here is the spring security settings.
security:
require-ssl: true
auth2:
client:
clientId: PROVIDED BY GOOGLE
clientSecret: PROVIDED BY GOOGLE
accessTokenUri: https://www.googleapis.com/oauth2/v4/token
userAuthorizationUri: https://accounts.google.com/o/oauth2/v2/auth
scope:
- openid
- email
- profile
resource:
userInfoUri: https://www.googleapis.com/oauth2/v3/userinfo
preferTokenInfo: true
server:
port: 8443
ssl:
key-store-type: PKCS12
key-store: /spring-config-server/host/tomcat-keystore.p12
key-alias: tomcat
key-store-password: ${KEYSTORE_PASSWORD}
Here are my dependencies from the POM file so you can see the version of the libraries I'm using.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.M8</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

I solve it implementing this WebSecurityConfigurer. It disables CSRF and set basic authentication.In Spring Boot 2.0.0 you cannot disable CSRF using properties it forces you to implement a java security config bean.
package my.package.config.server;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.anyRequest().authenticated().and()
.httpBasic();
;
}
}
Hope it helps

We must implement WebSecurityConfigurerAdapter in configuration related class. So that the encrypt/decrypt services can be accessible. Make sure that you have configured secret.key in bootstrap.properties or application.properties.

WebSecurityConfigurerAdapter is deprecated
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
Try the following instead of:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.anyRequest().authenticated().and()
.httpBasic();
return http.build();
}
}

To fix this issue, I needed to extend WebSecurityConfigurerAdapter and in the configure method I disabled CSRF token.
http
.csrf().disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**", "/error**")
.permitAll()
.anyRequest().authenticated();

Related

Okta-Spring Boot keeps throwing 400 response using company provided okta registration information

Our company provided me with new okta registration information. Specifically, the following :
okta.oauth2.issuer=https://purpleid-test.oktapreview.com/oauth2/aaabbbbccc
okta.oauth2.audience=ABC12345
okta.oauth2.client-id=0oaSomeClientId
In my spring boot application, I have the following :
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
....
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<version>3.1.4</version>
</dependency>
<dependency>
<groupId>com.okta.spring</groupId>
<artifactId>okta-spring-boot-starter</artifactId>
<version>2.1.6</version>
</dependency>
</dependencies>
....
</project>
Main class
....
import java.security.Principal;
#SpringBootApplication
public class MessageConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(MessageConsumerApplication.class, args);
}
#RestController
#CrossOrigin
static class RootEndpointRestController {
#GetMapping("/test")
String test(Principal principal){
return "test";
}
}
}
application.properties
management.endpoints.web.exposure.include=env,health,info,beans,refresh,bus-refresh
management.endpoint.health.show-details=ALWAYS
management.endpoint.refresh.enabled=true
server.port=8080
okta.oauth2.issuer=https://ourcompanyid-test.oktapreview.com/oauth2/aaabbbbccc
okta.oauth2.audience=ABC12345
okta.oauth2.client-id=0oaSomeClientId
SecurityConfiguration class
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
System.setProperty("https.proxyHost", "internet.proxy.ourcompany.com");
System.setProperty("https.proxyPort", "5555");
http.cors().and()
.authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated())
.oauth2ResourceServer().jwt();
}
}
Problem :
Whenever I try to access url like localhost:8080/test or localhost:8080/actuator in the browser (Chrome/Edge), it always returns 400 Bad Request
In the browser URL, this is what shows up
https://ourcompanyid-test.oktapreview.com/oauth2/aaabbbbccc
v1/authorize?
response_type=code&
client_id=0oaSomeClientId&
scope=profile%20email%20openid&
state=I2VByus9KqEt_Zt0ivvG9j_IKtLBldoQrZg-a1SRsYM%3D&
redirect_uri=http://localhost:8080/login/oauth2/code/okta&
code_challenge_method=S256&
nonce=AISpoMaYWFKjp2ZdF_xncSd8LFw7FKTMK9D6G1xbP3o&
code_challenge=hi1EmraZOfOYsdn5rolIaRZO4-pbA8yHtMpIVxjcDP0
However, when I use my personal Okta Developer account registration, it succeeds and redirects me to okta login page when I try to access localhost:8080/test or localhost:8080/actuator in the browser (Chrome/Edge)
My personal Okta Developer account registration looks something like this :
okta.oauth2.issuer=https://dev-123456.okta.com/oauth2/default
okta.oauth2.client-id=0oaMyClientId
okta.oauth2.client-secret=MyClientSecret
I don't have any idea why it would work using my personal Okta registration info and why it would NOT work when I use the Okta registration provided by our company.
Basically, when I use my personal Okta, it redirects to Okta login page just fine. But when I use office provided Okta registration, it returns 400 Bad Request
Is this something to do with my SecurityConfiguration class? Is my configuration class good?
Or, is this something that can be fixed by changing Okta configuration in Okta website?
NOTE: I was informed by the person who created the company okta registration that they created a Single-Page type of Okta registration.
I'd appreciate any comment, explanation or suggestion to fix this.
Thank you.
#RestControllers are OAuth2 resource-servers. Configure it as so. This can be as simple as:
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
<version>6.0.4</version
</dependency>
#EnableMethodSecurity
public static class WebSecurityConfig { }
com.c4-soft.springaddons.security.issuers[0].location=https://ourcompanyid-test.oktapreview.com/oauth2/aaabbbbccc
com.c4-soft.springaddons.security.issuers[0].authorities.claims=groups
com.c4-soft.springaddons.security.cors[0].path=/test
Follow link above for samples with more options (servlet VS reactive apps or JWT decoder VS access-token introspection, and last Spring default Authentictaion implementation VS custom ones), and also tips for testing (unit & integration) with mocked OAuth2 identities. Tutorials are providing with instructions for configuring resource-servers with just spring-boot-starter-oauth2-resource-server or spring-addons boot starters building on top of it (and much simplified Java configuration).

SpringBoot OAuth2 Client Google RequestMapping not working in RestController

I am trying to build a SpringBoot OAuth2 Application but stuck with initiating the next POST request of access_token.
This is my code https://github.com/sangeeta-p-shetty/springboot_oauth2_google.git
It runs on http://localhost:8080/security. Currently it successfully redirects to Google and on Sign In redirects to the Redirect URL
Primarily, I am stuck at 3 areas:
#RequestMapping in #RestController is not working.
As RequestMapping is not working, the sample code even though successfully authorizes I am not able to initiate the next POST request of AccessToken. Wanted guidance on how to capture the response from Google and initiate a new request.
I wanted to redirect to OAuth2 only if specific URL is hit in the application. Currently Redirection is happening by launch of context. i.e http://localhost:8080/security
SecurityConfig.java
package com.google.config;
import ....;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// only disable these during testing or for non-browser clients
.cors().disable()
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.loginPage("/oauth2/authorization/google")
.authorizationEndpoint()
.authorizationRequestResolver(
new CustomAuthorizationRequestResolver(
clientRegistrationRepository(), "/oauth2/authorization"));
}
}
application.yml
server:
port: 8080
servlet:
context-path: /security
spring:
security:
oauth2:
client:
registration:
google:
client-id: test.apps.googleusercontent.com
client-secret: test
redirect-uri: http://localhost:8080/security/welcome.html
authorize-uri: https://accounts.google.com/o/oauth2/v2/auth
scope: email
response-type: code
cookie-path-domain: /
cookie-secure: true
provider:
google:
issuer-uri: https://accounts.google.com
SpringBootApplication class
package com.google;
#SpringBootApplication
#ComponentScan("com.google")
public class OidcExampleApp {
public static void main(String[] args) {
SpringApplication.run(OidcExampleApp.class, args);
}
}
AppController.java
Currently in the github code, this class is not included. But below is the intended code which does not seem to get called.
package com.google.controller;
import ...;
#RestController
public class AppController {
#RequestMapping(value="/welcome") // here even If I give value="/welcome.html") it does not work
public String getHi() {
System.out.println("End Point hit*************************************");
return "Hi";
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.codetinkering</groupId>
<artifactId>spring-boot-oauth2-oidc-google</artifactId>
<version>1.0-SNAPSHOT</version>
<name>oauth2-example</name>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<finalName>security</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.google.OidcExampleApp</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Logs Screenshot

Spring Gateway and Auth0: IllegalArgumentException: Unable to find GatewayFilterFactory with name TokenRelay

Im trying to build a spring gateway which is getting JWT and is sending the tokens to all underlying services. For this I use the following dependencies:
<!-- Spring Boot Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<!-- Spring Boot Dependencies -->
<!-- Spring Cloud Dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Spring Cloud Dependencies -->
I configured my application for Auth0:
spring:
cloud:
gateway:
routes:
- id: my-service
uri: http://localhost:8001/
predicates:
- Path=/comments
filters:
- TokenRelay= #to send the token to the underlying service
- RemoveRequestHeader=Cookie #remove cookies since underlying services don't need them
security:
oauth2:
resourceserver:
jwt:
issuer-uri: #my issuer-uri
audience: #my audience
I implemented the audience validator and the jwt decoder like described here:
#Configuration
#ConditionalOnProperty(name = {"spring.security.oauth2.resourceserver.jwt.issuer-uri"})
public class AuthenticationOauth2Configuration {
#Value("${spring.security.oauth2.resourceserver.jwt.audience}")
private String audience;
#Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
#Bean(name = "customJwtDecoder")
public JwtDecoder getJwtDecoder() {
final NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(issuer);
final OAuth2TokenValidator<Jwt> audienceValidator = new JwtAudienceValidator(audience);
final OAuth2TokenValidator<Jwt> issuer = JwtValidators.createDefaultWithIssuer(this.issuer);
final OAuth2TokenValidator<Jwt> audience = new DelegatingOAuth2TokenValidator<>(issuer, audienceValidator);
jwtDecoder.setJwtValidator(audience);
return jwtDecoder;
}
}
public class JwtAudienceValidator implements OAuth2TokenValidator<Jwt> {
private final String audience;
public JwtAudienceValidator(final String audience) {
this.audience = audience;
}
#Override
public OAuth2TokenValidatorResult validate(Jwt jwt) {
final OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);
if (jwt.getAudience().contains(audience)) {
return OAuth2TokenValidatorResult.success();
}
return OAuth2TokenValidatorResult.failure(error);
}
}
However when Im starting the gateway service im getting the following error:
Caused by: reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalArgumentException: Unable to find GatewayFilterFactory with name TokenRelay
Caused by: java.lang.IllegalArgumentException: Unable to find GatewayFilterFactory with name TokenRelay
I literally cant find any resources on how to fix this.
You need org.springframework.boot:spring-boot-starter-oauth2-client as said here.
But I don't think you need it as soon as you use resource server. Gateway will forward your headers downstream without any configuration, so you will be able to find the authorization header there.
In order for spring cloud gateway to pass tokens to downstream services and validate tokens you need the following dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
To give an example to what Eduard Khachirov said:
dependencies:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
service application.yml:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://<AUTH0_DOMAIN>/
auth0:
audience: <AUTH0_API_AUDIENCE>
service security config:
#Configuration
#EnableWebSecurity
public class Oauth2ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Value("${auth0.audience}")
private String audience;
#Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
#Override
public void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
}
#Bean
public JwtDecoder jwtDecoder() {
final NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(issuer);
jwtDecoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
JwtValidators.createDefaultWithIssuer(issuer),
new AudienceValidator(audience)));
return jwtDecoder;
}
static class AudienceValidator implements OAuth2TokenValidator<Jwt> {
private final String audience;
public AudienceValidator(final String audience) {
this.audience = audience;
}
public OAuth2TokenValidatorResult validate(final Jwt jwt) {
if (jwt.getAudience().contains(audience)) {
return OAuth2TokenValidatorResult.success();
}
return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", "The required audience is missing", null));
}
}
}
gateway application.yml
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://<AUTH0_DOMAIN>/
cloud:
gateway:
routes:
- id: my-service
uri: lb://MY-SERVICE
predicates:
- Path=/api
loadbalancer:
ribbon:
enabled: false
gateway security config:
#Configuration
#EnableWebFluxSecurity
public class Oauth2ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
}
}
I had the same problem, I needed an OAuth2 consumer acts as a Client and forwards the incoming token to outgoing resource requests.
As I were using a Spring Cloud Gateway embedded reverse proxy then I could ask it to forward OAuth2 access tokens downstream to the services it is proxying. Thus the SSO app above can be enhanced simply like this (using TokenRelay Filter):
spring:
cloud:
gateway:
routes:
- id: resource
uri: http://localhost:9000
predicates:
- Path=/resource
filters:
- TokenRelay=
To enable this for Spring Cloud Gateway add the following dependencies
org.springframework.boot:spring-boot-starter-oauth2-client
org.springframework.cloud:spring-cloud-starter-security.
I had this pom.xml configuration:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-core</artifactId>
<version>${springdoc.openapi.webflux}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-ui</artifactId>
<version>${springdoc.openapi.webflux}</version>
</dependency>
</dependencies>

UserDetailsService ignored by security configuration

I have tried security configuration for spring webFlux as described in documentation: https://docs.spring.io/spring-security/site/docs/current/reference/html/jc-webflux.html#explicit-webflux-security-configuration
Security configuration ignores userDetailsServiceBean - i cannot login with user:pass, but may login with credentials from autoconfigurated
UserDetailsServiceAutoConfiguration:
2019-04-22 11:29:24.571 INFO 12343 --- [ main] .s.s.UserDetailsServiceAutoConfiguration :
Using generated security password: ff00a80a-d810-43d6-9c89-e861eb1bed96
My pom.xml (fragment):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
My security config:
#EnableWebFluxSecurity
#EnableWebSecurity
#Configuration
public class WebfluxSecurityConfig {
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.anyExchange().authenticated()
.and()
.httpBasic().and()
.formLogin();
return http.build();
}
#Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("pass")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
Why security config ignores userDetailsService ?
Is there error in spring-security documentation?
Remove the #EnableWebSecurity annotation. It's used for Servlet applications and isn't applied on WebFlux.
You might also consider defining a bean of type UserDetailsRepositoryReactiveAuthenticationManager in your WebFlux Configuration; such as the following:
#Bean
public ReactiveAuthenticationManager authenticationManager(ReactiveUserDetailsService detailsService) {
return new UserDetailsRepositoryReactiveAuthenticationManager(detailsService);
}
In your case, most probably, the #EnableWebSecurity annotation configures a bean of type InMemoryUserDetailsManager which is the non-reactive variant of ReactiveUserDetailsManager.
NOTE: You can remove the following from your POM if you're planning to use WebFlux only: spring-boot-starter-tomcat and spring-boot-starter-web

SpringSecurity gives error 401 Bad Credentials

I am new to Spring Security and was creating a simple app to check authentication and authorization. I am using in memory database. Even when I am giving correct login credentials, I am getting error 401 "Bad Credentials" error.
Also I used permitAll() function at some rest endpoints, yet I get a login prompt on those endpoints also. I tried clearing browser history and cache also yet no success. Please help. I am attaching code.
SecurityConfig.java
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
//Authentication using In Memory Database
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder)throws Exception{
authenticationManagerBuilder.inMemoryAuthentication()
.withUser("user").password("{noop}pass123").authorities("ROLE_USER")
.and()
.withUser("admin").password("{noop}pass123").authorities("ROLE_USER","ROLE_ADMIN");
}
//Authorization
#Override //Overriding configure to use HttpSecurity for web based HTTP requests
public void configure(HttpSecurity httpSecurity)throws Exception{
httpSecurity.
authorizeRequests()
.antMatchers("/protectedbyuserrole*").hasRole("USER")
.antMatchers("/protectedbyadminrole*").hasRole("ADMIN")
.antMatchers("/","/notprotected").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
SpringSecurityApplication.Java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
#SpringBootApplication
#ComponentScan({"com.example.demo","controller"})
#Import({SecurityConfig.class})
public class SpringSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityApplication.class, args);
}
}
TestSecurityController.Java
package com.example.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class TestSecurityController {
#RequestMapping("/")
public String Hello() {
return "Hello World!! ";
}
#RequestMapping("/notprotected")
public String HelloAgain() {
return "Hello from a non protected user!! ";
}
#RequestMapping("/protectedbyuserrole")
public String HelloUser() {
return "Hello User Role!! ";
}
#RequestMapping("/protectedbyadminrole")
public String HelloAdmin() {
return "Hello Admin Role!! ";
}
}
POM.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.19.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>SpringSecurity-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringSecurity-1</name>
<description>SpringSecurity for Authentication and Authorization</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
QUERIES
Also let me know how to use simple passwords which I can use in postman. Should I use {noop} or just simply writing the password like .password("pass123").
Should I use single * or ** asterisk in .antmatchers()
I tried it using Postman also and Firefox also. Same error everywhere.
POSTMAN SCREENSHOT
Specifying a particular method (GET, POST etc.) in RequestMapping is a good practice you may need to follow.
I have shared a basic example which I have done in the past.
You can try in the browser with username as myusername and password as a mypassword
If you still face the problem let me know with your postman screenshot
#Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.withUser("myusername")
.password("mypassword")
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.authorizeRequests().antMatchers("/login","/home","/failure").permitAll()
.antMatchers(HttpMethod.POST,"/admin/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT,"/admin/**").hasRole("ADMIN")
.antMatchers(HttpMethod.GET,"/admin/**").hasRole("ADMIN")
.antMatchers(HttpMethod.GET,"/user/**").hasAnyRole("ADMIN","USER")
.antMatchers(HttpMethod.POST,"/user/**").hasAnyRole("ADMIN","USER")
.anyRequest().authenticated();
}
EDIT
The mapping matches URLs using the following rules:
? matches one character
matches zero or more characters
** matches zero or more directories in a path
{spring:[a-z]+} matches the regexp [a-z]+ as a path variable named "spring"
Refer Path Matcher Here
#RequestMapping(value="/protectedbyuser",method=RequestMethod.GET)
public String Hello() {
return "Hello Protected By User!! ";
}

Resources