I have the following...
@RestController
@RequestMapping(path="/person", produces = MediaType.APPLICATION_JSON_VALUE)
public class AuthController {
@GetMapping("")
@ResponseBody
public String getPersonFromEmail(@RequestParam(name = "email") String email){ ... }
}
@EnableWebSecurity
public class SecurityConfig {
...
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.requestMatchers("/person").hasAuthority("SCOPE_blahablah")
.anyRequest().authenticated()
.and().cors()
.and().oauth2ResourceServer().jwt();
return http.build();
}
}
When I run but pass no token I get a 401. However, when I pass a token that doesn't have the proper scope I get a 200. I would expect to also get a 403. What am I missing?
With a little more digging it looks like the problem is the query params. Not sure how to acoomadate those...
~/tmp/user-profile >curl -v http://localhost:8080/person
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /person HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.85.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 403
< Set-Cookie: JSESSIONID=EFD9C400D91760FE8FA2119AC2EB382B; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Wed, 11 Jan 2023 21:45:16 GMT
<
* Connection #0 to host localhost left intact
~/tmp/user-profile >curl -v http://localhost:8080/person?email=...
zsh: no matches found: http://localhost:8080/person?email=... <-- This means it worked without checking
What you should expect is actually a 403 (forbidden which mean that authentication is valid but access was denied) and not a 401 (unauthorized which means that authentication is missing or invalid).
Have you checked that your SecurityConfig
is correctly loaded with a breakpoint or log line? It Seams that @Configuration
is missing and my bet is that your resource-server is actually secured with spring-boot default SecurityFilterChain
which only applies anyRequest().authenticated()
.
You could also disable sessions (and CSRF protection). Access could be denied because of CSRF protection which is useless when sessions are disabled and sessions are rarely needed with access-tokens. It would also ease scaling and fault tolerance.
You could have a look at my tutorials for resource-server configuration and access-control tests.
package com.c4soft.dumb;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@SpringBootApplication
public class DumbApplication {
public static void main(String[] args) {
SpringApplication.run(DumbApplication.class, args);
}
@RestController
@RequestMapping(path = "/dumb")
public class AuthController {
@GetMapping("/public")
public String getPublic() {
return "Hello!";
}
@GetMapping("/person")
public String getPersonFromEmail(@RequestParam(name = "email") String email) {
return email;
}
@GetMapping("/name")
public String getName(Authentication auth) {
return auth.getName();
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.oauth2ResourceServer().jwt();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().csrf().disable();
// @formatter:off
http.authorizeHttpRequests()
.requestMatchers("/dumb/public").permitAll()
.requestMatchers("/dumb/person").hasAuthority("SCOPE_email")
.anyRequest().authenticated();
// @formatter:on
http.cors().configurationSource(corsConfigurationSource());
return http.build();
}
private CorsConfigurationSource corsConfigurationSource() {
final var configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setExposedHeaders(Arrays.asList("*"));
final var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/dumb/**", configuration);
return source;
}
}
}
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-ch4mpy.eu.auth0.com/
Queries with Postman work exactly as expected:
http://localhost:8080/dumb/public
is accessible without any authentication http://localhost:8080/dumb/name
return
401
if access token is missing or invalid (wrong issuer, expired, ...) sub
claim value if access token is validhttp://localhost:8080/dumb/person?email=dumb@truc.com
returns
401
if access token is missing or invalid (wrong issuer, expired, ...) 403
if client did not request email
scope dumb@truc.com
if access-token is valid and has email
scope
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.