#RestController
public class ApplicationController {
#PermitAll
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index() {
return "Greetings from ContextConfig Boot!";
}
#RolesAllowed({"ADMIN"})
#RequestMapping(value = "/secured", method = RequestMethod.GET)
public String secured() {
return "Secured :)";
}
}
Token is send in header "X-AUTH-TOKEN".
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
}
}
This actual spring security configuration. How to configure spring security when user send token in header and hase role "ADMIN" he will be allowed to access "secured"?
Related
Below is my web security config class,
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "authority")
#Profile({"cloud", "development", "release"})
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private Map<String, String> data = new LinkedHashMap<>();
private List<String> whiteList = new ArrayList<>();
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/v1/students").authenticated()
.and().authorizeRequests().antMatchers("/api/v1/students/*").authenticated()
.and().authorizeRequests().antMatchers("/api/v1/students/*").access("hasRole('learning_management_admin_app1000000')")
.and().authorizeRequests().antMatchers("/api/v1/students").access("hasRole('learning_management_admin_app1000000')");
http.csrf().disable().authorizeRequests().antMatchers(getWhiteList().toArray(new String[0]))
.permitAll();
http.cors();
Okta.configureResourceServer401ResponseBody(http);
}
public List<String> getWhiteList() {
..
And In the controller,
#Validated
#CrossOrigin("*")
#RestController
#RequestMapping("/api/v1")
#SecurityRequirement(name = "BearerAuth")
#PreAuthorize("isAuthenticated()")
public class StudentsController {
#GetMapping("change-requests")
#PreAuthorize("hasAuthority('learning_management_admin_app1000000')")
public ResponseEntity<List<Student>> getStudents(
#RequestParam(required = false) String name) {
Here even with a valid token, it throws 401 Unauthorized exception from every endpoint. Even in the swagger ui, it throws 401 unauthorized with a valid token.
I am experiencing a weird problem, When multiple concurrent requests comes to a controllerSecurityContextHolder.getContext().getAuthentication().getPrincipal()
returns same user object sometimes even if the JWT token is different.
#RequestMapping(value = {"/users/{userId}/solveDetail/create"}, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#Transactional
public ResponseEntity<CreateSolveDetail> createSolve(#PathVariable("userId") Long userId, #RequestBody CreateSolveDetail createSolveDetail){
User user =SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
So far tried changing session management to .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) and thread strategy is set to SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_THREADLOCAL) still the isssue persists.
Below is the WebSecurityConfig class configured and a custom filter is added which overrides getPreAuthenticatedPrincipal and getPreAuthenticatedPrincipal of AbstractPreAuthenticatedProcessingFilter class.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityBasicConfig {
#Autowired
private Http403ForbiddenEntryPoint http403ForbiddenEntryPoint;
#Bean
public Http403ForbiddenEntryPoint http403ForbiddenEntryPoint() {
return new Http403ForbiddenEntryPoint();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.exceptionHandling()
.authenticationEntryPoint(http403ForbiddenEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(preAuthFilter(), BasicAuthenticationFilter.class);
httpSecurity.csrf().disable();
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_THREADLOCAL);
}
}
public class PreAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpServletRequest) {
String auth = httpServletRequest.getHeader("PRE-AUTH");
try {
User user = new ObjectMapper().readValue(auth, User.class);
return user;
} catch (Exception e) {
return new User();
}
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest httpServletRequest) {
String auth = httpServletRequest.getHeader("PRE-AUTH");
return auth;
}
}
Please let me know what I am doing wrong here.
Thanks in advance.
Spring boot version : 2.1.6.RELEASE
Architecture: Microservice
I've started with the following file to config URLs and protected paths using Spring Security and OAuth2:
#EnableResourceServer
#RestController
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
{
#RequestMapping("/publica")
public String publico() {
return "Pagina Publica";
}
#RequestMapping("/privada")
public String privada() {
return "Pagina Privada";
}
#RequestMapping("/admin")
public String admin() {
return "Pagina Administrador";
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/oauth/token", "/oauth/authorize**", "/publica").permitAll();
http
.requestMatchers().antMatchers("/privada")
.and().authorizeRequests()
.antMatchers("/privada").access("hasRole('USER')")
.and().requestMatchers().antMatchers("/admin")
.and().authorizeRequests()
.antMatchers("/admin").access("hasRole('ADMIN')");
}
}
This works fine. If I try to access /privada in postman it returns a 401.
However, for the app I'm planning to build from this I figured it would be better to organise URLs in their own controllers (e.g. FundsController, UsersController, ProductsController etc)
So, as a basic example from the above I'm moving the path mapping methods out into BasicController:
#RestController
public class BasicController
{
#RequestMapping("/publica")
public String publico() {
return "Pagina Publica";
}
#RequestMapping("/privada")
public String privada() {
return "Pagina Privada";
}
#RequestMapping("/admin")
public String admin() {
return "Pagina Administrador";
}
}
But leaving the security stuff in the ResourceServerConfiguration:
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
{
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/oauth/token", "/oauth/authorize**", "/publica").permitAll();
http
.requestMatchers().antMatchers("/privada")
.and().authorizeRequests()
.antMatchers("/privada").access("hasRole('USER')")
.and().requestMatchers().antMatchers("/admin")
.and().authorizeRequests()
.antMatchers("/admin").access("hasRole('ADMIN')");
}
}
But now when I restart the app (in-memory access tokens destroyed) then go to /privada it returns Pagina Privada which is Spanish I think for "Private page" :) There was no access token required anyway, which is not what I wanted. It should return a 401 as it did before when it was all within the same class. Where have I gone wrong?
Add
#Configuration
#EnableWebSecurity
annotations to your ResourceServerConfiguration class and
extend WebSecurityConfigurerAdapter instead of ResourceServerConfigurerAdapter
You need to have below in the configure method you override
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/context"))
.antMatchers("/context/**").fullyAuthenticated();
I wanted to call https://oauth.reddit.com/api/v1/me endpoint, so I created follwing REST controller:
#RestController
#RequestMapping("/reddit")
public class RedditController {
#Autowired
private OAuth2RestTemplate redditRestTemplate;
#Value("${secured.service.url:https://oauth.reddit.com/api/v1/me}")
private String endpoint;
#RequestMapping(value = "/message", method = RequestMethod.GET)
public String getMessageFromSecuredService(){
ResponseEntity<String> entity = redditRestTemplate.getForEntity(endpoint, String.class);
return entity.getBody();
}
}
To configure authentication I created following configuration:
#Configuration
#EnableOAuth2Client
#EnableWebSecurity
public class KeycloakClientCredentialsConfig extends WebSecurityConfigurerAdapter {
//...
#Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring().antMatchers("/**");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
super.configure(http);
}
#Bean
public OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("reddit");
details.setClientId(clientId);
details.setClientSecret(clientSecret);
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setScope(Arrays.asList("identity", "edit", "flair", "history", "modconfig", "modflair", "modlog", "modposts", "modwiki", "mysubreddits", "privatemessages", "read", "report", "save", "submit", "subscribe", "vote", "wikiedit", "wikiread"));
details.setPreEstablishedRedirectUri("http://localhost:8080");
details.setUseCurrentUri(false);
return details;
}
#Bean
public OAuth2RestTemplate createRestTemplate(OAuth2ClientContext clientContext) {
return new OAuth2RestTemplate(oAuth2ProtectedResourceDetails(), clientContext);
}
}
However each time I am not getting JSON result, but HTML page so it seems that authentication didn't work.
Do you know if my configuration is not set correctly?
Maybe my REST template should be built on configuration for invoking refresh token endpoint instead of authorize endpoint?
Here is my code:
#Configuration
#ComponentScan(basePackages = "com.webapp")
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests().antMatchers("/resources/**").permitAll().
antMatchers("/admin/**").hasRole("ADMIN").
anyRequest().authenticated().
and().
formLogin().loginPage("/login").permitAll().
and().
logout().permitAll();
}
#Autowired
public void configureGlobal(UserDetailsService userDetailsService, AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService);
}
}
when a request /admin/* comes in, it will verify if the user has admin role by calling "antMatchers("/admin/**").hasRole("ADMIN")." , but in my controller, it does not check if the user has other permissions with #PreAuthorize .
#Controller
#SessionAttributes({ "user" })
#RequestMapping(value = "/admin/user")
public class UserController {
static Logger logger = LoggerFactory.getLogger(UserController.class);
#Autowired
private RoleDAO roleDao;
#Autowired
private MessageSource messageSource;
#Autowired
private UserDAO userDao;
#RequestMapping(value = { "/", "/list" }, method = RequestMethod.GET)
#PreAuthorize("hasRole('USER_VIEW')")
public ModelAndView listUsers() {
List<User> users = userDao.list();
ModelAndView model = new ModelAndView("/admin/user/user-list");
model.addObject("users", users);
if (model.getModel().get("user") == null) {
model.getModel().put("user", new User());
}
this.loadRoles(model);
return model;
}
}
Normally, Spring Security becomes available in the root application context and Spring MVC beans are initialized in a child context.
Hence org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor can't detect your controller beans because they live in a child context that is unknown to the root context.
#EnableGlobalMethodSecurity or <global-method-security> has to be placed inside the same configuration class or xml file where your Spring MVC configration lives in order to enable #PreAuthorize and #PostAuthorize.
try to add #EnableGlobalMethodSecurity(prePostEnabled = true) above your Security configuration class. It is works to me!