I'm trying to secure a simple Spring-Data-Rest app using jwt.
Taking the seed from https://github.com/spring-projects/spring-data-examples/tree/master/rest/security
The SecurityConfig is below (using normal username, password authentication)
How can I change this to JWT Authentication?
(Authorization is already done using #PreAuthorize("hasRole('ROLE_USER')") in repositories)
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* This section defines the user accounts which can be used for
* authentication as well as the roles each user has.
*/
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("greg").password("turnquist").roles("USER").and()
.withUser("ollie").password("gierke").roles("USER", "ADMIN");
}
/**
* This section defines the security policy for the app.
* - BASIC authentication is supported (enough for this REST-based demo)
* - /employees is secured using URL security shown below
* - CSRF headers are disabled since we are only testing the REST interface,
* not a web one.
*
* NOTE: GET is not shown which defaults to permitted.
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/employees").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/employees/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PATCH, "/employees/**").hasRole("ADMIN").and()
.csrf().disable();
}
}
here is a good tutorial for JWT Authentication in spring boot, but in can applied for spring applications as well: https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/
According to the tutorial in your SecurityConfiguration.configure you need
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter, is applied to /login URL and generates JWT token based on your login/password if such a user exists in the system.
JWTAuthorizationFilter verifies JWT token coming in http header
Of course you need to add more moving parts in order to enable JWT auth by this tutorial.
I followed the tutorial for Spring Security OAuth:
https://projects.spring.io/spring-security-oauth/docs/oauth2.html
In particular you have to enable the resource server. This is my (modified) configuration):
#EnableResourceServer
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenServices());
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
try {
// Load the public key of the authorization server.
String key = IOUtils.toString(getClass().getResource("/reng0-public.key"), Charset.forName("US-ASCII"));
converter.setVerifierKey(key);
} catch (IOException e) {
throw new RuntimeException(e);
}
return converter;
}
#Bean
#Primary
public ResourceServerTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
}
The client has to add the Authorization: Bearer header to make it work.
Related
What I'm trying to do is just authenticate in-memory default user using a custom authentication filter that parses a JSON payload that contain the username and the password.
SecurityConfig.java
package ali.yousef.authdemo.config.security;
#Configuration
#EnableWebSecurity
public class SecurityConfig
{
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception
{
AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();
return authenticationManager;
}
#Bean
PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception
{
JsonUserPasswordAuthenticationFilter jsonUserPasswordAuthenticationFilter = new JsonUserPasswordAuthenticationFilter();
jsonUserPasswordAuthenticationFilter.setAuthenticationManager(authenticationManager);
http
.csrf().disable()
.formLogin().disable()
.addFilterAt(jsonUserPasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests()
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll();
return http.build();
}
}
JsonUserPasswordAuthenticationFilter.java
package ali.yousef.authdemo.config.security;
public class JsonUserPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{
protected JsonUserPasswordAuthenticationFilter(AuthenticationManager authenticationManager)
{
this.setAuthenticationManager(authenticationManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException
{
UsernamePasswordDto usernamePasswordDto;
try
{
usernamePasswordDto = new ObjectMapper().readValue(request.getInputStream(), UsernamePasswordDto.class);
System.out.println(usernamePasswordDto.toString());
}
catch (IOException ioe)
{
throw new AuthenticationServiceException(ioe.getMessage(), ioe);
}
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(usernamePasswordDto.getUsername(), usernamePasswordDto.getPassword());
return this.getAuthenticationManager().authenticate(authToken);
}
}
TestController.java
#RestController
public class TestController
{
#GetMapping("/api/hello")
public String hello(Principal principal)
{
return "hello " + principal.getName();
}
}
When authenticating the default user it gets authenticated and return the home page but when I try to send a request to /api/hello it respond with 403.
EDIT:
I edited how I register the custom authentication filter. But the same problem is present. It seems like the security context gets cleared after successful authentication and I get anonymousUser from principal.
Extending a UsernamePassWordAuthenticationFilter brings in more customizations than you need to set up a custom auth filter.. see this post for more details why your filter is not getting called - link
You can achieve the same using OncePerRequestFilter as below -
#Component
public class JsonUserPasswordAuthenticationFilter extends OncePerRequestFilter
{
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
UsernamePasswordAuthenticationToken authToken = YOUR_LOGIC(); // new UsernamePasswordAuthenticationToken("test", "passwd",Collections.emptyList()); as you have no authorities empty list is important here...
SecurityContextHolder.getContext().setAuthentication(authToken);
filterChain.doFilter(request, response);
}
}
#Configuration
#EnableWebSecurity
public class SecurityConfig
{
#Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, JsonUserPasswordAuthenticationFilter filter) throws Exception {
http
.csrf().disable()
.formLogin().disable()
.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(auth -> {
auth.requestMatchers(new AntPathRequestMatcher("/api/**")).authenticated()
.anyRequest().permitAll();
}) ;
return http.build();
}
When you send a request to /api/hello your filter’s attemptAuthentication() never takes action. This is because your custom implementation extends the UsernamePasswordAuthenticationFilter (which in its turn extends the AbstractAuthenticationProcessingFilter). The UsernamePasswordAuthenticationFilter, by default, is used for .formLogin authentication, and handles the default AntRequestMatcher "/login". Sending a request to /api/hello is an endpoint that is not handled by your filter. Since your security configuration requires that any /api/** endpoint should be authenticated, you receive the error. So, it should be made clear that an implementation of either UsernamePasswordAuthenticationFilter or AbstractAuthenticationProcessingFilter, is usually being used for Authenticating a user.
However, you can use/add your testing /api/hello endpoint to your filter and confirm that it works. For instance, you can override the default “/login” AntRequestMatcher from within your custom filter constructor, by using something like that (use the appropriate for you Http action, GET, POST, etc:
protected JsonUserPasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
this.setAuthenticationManager(authenticationManager);
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/hello", "POST"));
}
I am trying to set up multiple WebsecurityConfigurerAdapter for my project where the spring boot actuator APIs are secured using basic auth and all other endpoints are authenticated using JWtAuthentication. I am just not able to make it work together, only the config with the lower order works. I am using Spring Boot 2.1.5.RELEASE
Security Config One with JWT Authenticator
#Order(1)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_WHITELIST = {
"/docs/**",
"/csrf/**",
"/webjars/**",
"/**swagger**/**",
"/swagger-resources",
"/swagger-resources/**",
"/v2/api-docs"
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.antMatchers("/abc/**", "/abc/pdf/**").hasAuthority("ABC")
.antMatchers("/ddd/**").hasAuthority("DDD")
.and()
.csrf().disable()
.oauth2ResourceServer().jwt().jwtAuthenticationConverter(new GrantedAuthoritiesExtractor());
}
}
The basic Auth config with username/password
#Order(2)
#Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
/* #Bean
public UserDetailsService userDetailsService(final PasswordEncoder encoder) {
final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(
User
.withUsername("user1")
.password(encoder.encode("password"))
.roles("ADMIN")
.build()
);
return manager;
}
#Bean PasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/actuator/**").hasRole("ADMIN")
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("password").authorities("ADMIN");
}
}
I have been trying to make it work for many days but cannot make both of them work together. If i swap the order, only basic auth works and not the JWT Auth Manager.
I have gone through a lot of SOF Questions, like
[https://stackoverflow.com/questions/40743780/spring-boot-security-multiple-websecurityconfigureradapter][1]
[https://stackoverflow.com/questions/52606720/issue-with-having-multiple-websecurityconfigureradapter-in-spring-boot][1]
[https://github.com/spring-projects/spring-security/issues/5593][1]
[https://www.baeldung.com/spring-security-multiple-entry-points][1]
Nothing seems to be working, is this a known issue in Spring?
To use multiple WebsecurityConfigurerAdapter, you need restrict them to specific URL patterns using RequestMatcher.
In your case you can set a higher priority for ActuatorSecurityConfig and limit it only to actuator endpoints:
#Order(-1)
#Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/actuator/**")
.and()
.authorizeRequests().anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
Hello i have been trying to configure spring to have it return JWT token if user/pass is authenticated to LDAP Server; Consider the use case below ;
On the above diagram, i have configured WebSecurity to check/filter out requests with Bearer. See code below
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
JwtAuthorizationTokenFilter authenticationTokenFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
// Configure Web Security
// Allow only /auth/
// Disallow all others
http
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST,
"/auth/**")
.permitAll()
.anyRequest().authenticated();
//Custom JWT
http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// disable page caching
http.headers().cacheControl();
}
}
AuthCtrl.java
#RestController
#RequestMapping("auth")
public class AuthCtrl {
private static final Logger logger = LoggerFactory.getLogger(AuthCtrl.class);
#Autowired
#Qualifier("authenticationManagerImpl")
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
#Qualifier("userDetailsServiceImpl")
private UserDetailsService userDetailsService;
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody String post(#RequestBody Map<String, String> credentials) {
logger.info("POST: {} | {} ",credentials.get("username"), credentials.get("password"));
String username = credentials.get("username");
String password = credentials.get("password");
Objects.requireNonNull(username);
Objects.requireNonNull(password);
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
// Reload password post-security so we can generate the token
final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
final String token = jwtTokenUtil.generateToken(userDetails);
return token;
} catch (DisabledException e) {
throw new AuthenticationException("User is disabled!", e);
} catch (BadCredentialsException e) {
throw new AuthenticationException("Bad credentials!", e);
}
}
#ExceptionHandler({AuthenticationException.class})
public ResponseEntity<String> handleAuthenticationException(AuthenticationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
}
}
Above configuration was based on a youtube guide i've seen and also a pull from a demo source in git. Great help!, credits to the owners. Got to understand how filters work somehow.
The above source can already filter out all protected API and sends out unauthorized back as a response when it is not authorized. The only api i allowed to be accessed anonymously is the authentication api /auth. It can already receive the request and passed through the web filters.
But i can't quite figure out how to authenticate the said request to LDAP server and sends out a JWT token. On the guide i've read they are getting the user information on a database.
I've read some documentation on LDAP configuration in WebConfiguration, but i can't relate it to my current filters.
Please check the below link I have created it using spring 4.
Instead of .ldif file on classpath configure your own ldap server.
https://github.com/merugu/springsecurity/tree/master/ldapauthenticationjwttoken
The only differences is for Spring 5 you should use
advance password encoding algorithm like Bcryptpasswordencoder.As the LDAPpasswordEncoder is deprecated.
Happy coding!
This question already has answers here:
When to use Spring Security`s antMatcher()?
(2 answers)
Closed 5 years ago.
I am trying to get Spring Security's basic authentication to work side by side with JWT token authentication with no success. I have implemented basic authentication for my web console and JWT to secure a number of API endpoints. Here's my config:
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MultiHttpSecurityConfig {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(this.userDetailsService)
.passwordEncoder(bCryptPasswordEncoder());
}
#Bean
public PasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
/**
*
* API Security configuration
*
*/
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter{
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter();
}
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/api/**","/refresh/**").authenticated()
.antMatchers("/auth/**").permitAll().anyRequest().authenticated();
// Custom JWT based security filter
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// disable page caching
httpSecurity.headers().cacheControl();
}
}
/**
*
* Form login security configuration
*
*/
#Configuration
public static class FormLoginWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ConsoleAuthenticationEntryPoint consoleAuthenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().exceptionHandling().authenticationEntryPoint(
consoleAuthenticationEntryPoint).and()
.authorizeRequests().antMatchers("/console/**").authenticated()
.antMatchers(HttpMethod.GET,
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().defaultSuccessUrl("/console/home")
.loginPage("/console/login")
.permitAll()
.and()
.logout()
.permitAll();
http.csrf().disable();
}
}
}
I have noticed that the configuration I annotate with Order(1) is the one that is picked by Spring Security and the other is completely ignored. Like in the above config, I get 401 error if I try to access /console/login.
Any help would be much appreciated.
The reason why is because neither ApiWebSecurityConfigurationAdapter nor FormLoginWebSecurityConfig uses the antMatcher(). This means that both security configurations will handle all paths, even though you're using antMatchers() afterwards. Due to this, the configuration with the lowest order (#Order(1)) will handle everything, while the other one will do nothing.
This is also mentioned in the docs:
The http.antMatcher states that this HttpSecurity will only be applicable to URLs that start with /api/
So, to fix this problem, you have to povide an antMatcher to one of your configurations (or both). For example, if the form login should only be applied to /console/login and /console/home, you could change the configuration to:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/console/**") // Add this
.httpBasic().and()
.exceptionHandling().authenticationEntryPoint(consoleAuthenticationEntryPoint).and()
.authorizeRequests().antMatchers("/console/**").authenticated()
.antMatchers(HttpMethod.GET,
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js").permitAll()
.anyRequest().authenticated().and()
.formLogin().defaultSuccessUrl("/console/home")
.loginPage("/console/login").permitAll().and()
.logout().permitAll().and() // Make sure to use .and() to add the .csrf()
.csrf().disable();
}
Another good read about this topic is this question: When to use Spring Security`s antMatcher()?
Please note that you shouldn't use the http builder twice like you did to add the .csrf().disable(), add it to the other builder like I did in the code above.
Also be aware that you'll likely have to change the order. You should put the order on the configuration with the most detailed antMatcher(), in this case FormLoginWebSecurityConfig.
Please I need help on a project I'm working on. It's a Spring Boot project and I've implemented Spring Boot Security OAuth2 and everything works just fine when the OAuth2 is mounting on the root (http://localhost:8080/oauth/token) but I need everything to be accessed via (http://localhost:8080/cranoo/api/v1/oauth/token) so I read online that I have to alter the mappings of the AuthorizationServerEndpointsConfigurer bean which I did. After I did that, the OAuth2 worked fine but other endpoints like the user accounts endpoints do not work. So I found a way to configure all endpoints using a ServletRegistrationBean and those endpoints work but the OAuth2 token endpoints stop to work and return 401. I expected that when the ServletRegistrationBean is configured with mapping everything coming from the app should be mounted on those mappings but it is not so.
Below is my code for the Dispatcher servlet.
#Bean
public ServletRegistrationBean dispatcherServletRegistration() {
Collection<String> mappings = new ArrayList<String>();
mappings.add("/cranoo/api/v1/*");
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet());
registration.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
registration.setUrlMappings(mappings);
return registration;
}
This is expected to make all endpoints accessed via http://localhost:8080/cranoo/api/v1 but when I use postman to access http://localhost:8080/cranoo/api/v1/oauth/token it returns 401 Unauthorized "Full authentication is required to access this resource"
But when I visit http://localhost:8080/cranoo/api/v1/cities it works and I get a list of all cities.
Below is my WebSecurityConfigurerAdapter bean.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
static final String SIGNING_KEY = "anietie100THjjj";
static final Integer ENCODING_STRENGTH = 256;
static final String SECURITY_REALM = "Sprinb Boot JWT Example Realm";
#Autowired
private UserDetailsService userDetailsService;
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(new ShaPasswordEncoder(ENCODING_STRENGTH));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/metrics/**", "/actuator/**", "/api-docs/**", "/login", "/register", "/oauth/token").permitAll()
.and()
.httpBasic()
.realmName(SECURITY_REALM)
.and()
.csrf()
.disable();
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY);
return converter;
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
Below is my AuthorizationServerEndpointsConfigurer code.
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
String context = "/cranoo/api/v1";
enhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
endpoints.pathMapping("/oauth/token", context + "/oauth/token");
endpoints.pathMapping("/oauth/authorize", context + "/oauth/authorize");
endpoints.pathMapping("/oauth/confirm_access", context + "/oauth/confirm_access");
endpoints.pathMapping("/oauth/check_token", context + "/oauth/check_token");
endpoints.pathMapping("/oauth/token_key", context + "/oauth/token_key");
endpoints.pathMapping("/oauth/error", context + "/oauth/error");
endpoints.tokenStore(tokenStore)
.accessTokenConverter(accessTokenConverter)
.tokenEnhancer(enhancerChain)
.authenticationManager(authenticationManager);
}
My thinking is that since I've configured the mapping for the entire application at the DispatcherServlet I shouldn't have to put these endpoints mapping again on the AuthorizationServerEndpointsConfigurer, and yet even if I remove all the mappings above it still doesn't work. It still returns 401. Please I need help as I've spent about four days on this and no progress.
All I want is to be able to access the token endpoint at (http://localhost:8080/cranoo/api/v1/oauth/token and other endpoints at (http://localhost:8080/cranoo/api/v1/{endpoint}