Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security allow each user to see their own profile but none else

In Spring MVC with Spring Security, is it possible to achieve this?

@Override WebSecurityConfigurerAdapter.configure(HttpSecurity)

@Override
protected void configure(HttpSecurity http) throws Exception
{
    http
            .authorizeRequests()
            .mvcMatchers("/users/{authentication.principal.username}").hasAnyRole(ADMIN, MANAGER)
            .antMatchers("/users/**").hasRole(ADMIN)
            .anyRequest().authorized()
    ...
}

/users/** is a restricted area and should be accessible by admins only. But managers should still be able to see their own profile (/users/user_with_manager_role), and only their own profile, not those of any other users (regardless of their role).


Solution

I've found a solution in Andrew's answer. My Code now looks like this:

WebSecurityConfigurerAdapter

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // added this annotation
public class SecurityConfig extends WebSecurityConfigurerAdapter

@Override WebSecurityConfigurerAdapter.configure(HttpSecurity)

@Override
protected void configure(HttpSecurity http) throws Exception
{
    http
            .authorizeRequests()
            // removed /users handling
            .anyRequest().authorized()
    ...
}

UsersController

@Controller
@RequestMapping("/users")
public class UsersController
{
    @GetMapping("{username}")
    @PreAuthorize("authentication.principal.username == #username) || hasRole('ADMIN')")
    public String usersGet(@PathVariable("username") String username)
    {
        // do something with username, for example get a User object from a JPA repository
        return "user";
    }
}
like image 476
Impulse The Fox Avatar asked Oct 11 '18 10:10

Impulse The Fox


People also ask

How do you allow a user only access their own data in Spring boot?

In any @Controller , @RestController annotated bean you can use Principal directly as a method argument. @RequestMapping("/users/{user_id}") public String getUserInfo(@PathVariable("user_id") Long userId, Principal principal){ // test if userId is current principal or principal is an ADMIN .... }

How do I bypass WebSecurityConfigurerAdapter?

Step 1: Add the security jar or dependency in your application. Step 2: Create a security config class and extend the WebSecurityConfigurerAdapter class. Step 3: Add the annotation @EnableWebSecurity on top of the class. Step 4: For authentication, override the method configure(AuthenticationManagerBuilder auth) .

What should I use instead of WebSecurityConfigurerAdapter?

You need to declare SecurityFilterChain and WebSecurityCustomizer beans instead of overriding methods of WebSecurityConfigurerAdapter class.


1 Answers

I'm afraid it's not possible: when this configuration is being set up, it has no info about {authentication.principal.username} which will be resolved at some point in future.

But Spring gives you a bunch of built-in method security expressions you can annotate your methods with.

Starting from a simple expression like @PreAuthorize("hasRole('ADMIN')"), you might end up with a custom one:

@XMapping(path = "/users/{username}")
@PreAuthorize("@yourSecurityService.isMyPage(authentication.principal, #username)")
public void yourControllerMethod(@PathVariable String username);

@yourSecurityService.isMyPage(authentication.principal, #username) refers to your @Service method public boolean isMyPage(Principal, String).

like image 142
Andrew Tobilko Avatar answered Nov 09 '22 09:11

Andrew Tobilko