Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring security allow access only users with specific username

I am developing a spring boot app and writing an API for users to be able to read messages. One of the URL's is:

/users/USER1/messages

Now, I obviously want only authenticated users to be able to access the content of this get request. But all authenticated users is not enough. I also want that only user who has a username - USER1 will be able to view the real content here, rest should receive 403 statuses. I figured out how to do it without spring security config (in my service I am checking logged in username and comparing it to the parameter in the URL, proceeding only if they're equal), but I think there should be a more simple way using just SecurityConfiguration? My current configuration looks like this:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers(HttpMethod.GET, "/users/**").authenticated()
            .antMatchers("/h2-console/*").permitAll()
                .anyRequest().authenticated()
            .and()
                .formLogin();

        http.csrf().disable();
        http.headers().frameOptions().disable();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("superman").password("superman").roles("USER")
                .and()
                .withUser("admin").password("admin").roles("ADMIN");
    }
}

EDIT: Following answer suggesting Method Security Expressions I have used it but it still seems not to work (if I am authenticated as USER2 I can still read messages for USER1). Here's my controller where I have added PreAuthorize annotation

@RequestMapping(method = RequestMethod.GET, value = "/messages", produces = {"application/json"})
@ResponseBody
@PreAuthorize("#userHandle == authentication.name")
public List<Message> getMessages(@PathVariable("userHandle") String userHandle,
                        @RequestParam(value="search", defaultValue="") String search) {
    //todo: change to return DTO not model
    return messageFacade.getMessages(userHandle, search);
}

EDIT2: As in comments in accepted answer @EnableGlobalMethodSecurity(prePostEnabled=true) needs to be included in security config. Once that's included everything is working fine.

like image 285
Paweł Laskowski Avatar asked Jan 30 '23 23:01

Paweł Laskowski


1 Answers

I think you need Spring Method Security. The example in the documentations is almost literally your case:

import org.springframework.data.repository.query.Param;

...

@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);

PS: Don't forget @EnableGlobalMethodSecurity! See a tutorial here

like image 63
Assen Kolov Avatar answered Feb 02 '23 11:02

Assen Kolov