简体   繁体   中英

@RolesAllowed in conjuction with configureGlobalSecurity jdbcAuthentication in Spring boot

@RolesAllowed does not seem to work when im using jdbcAuthentication() , but it works fine if I am using inMemoryAuthentication() . jdbcAuthentication() works fine if I remove @RolesAllowed .

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.
            jdbcAuthentication()
            .usersByUsernameQuery("select u.email, u.password, u.enabled from bagtag.user u where email=?")
            .authoritiesByUsernameQuery("select u.email, r.name from bagtag.user u join bagtag.role r on (r.id = u.role_id) where u.email=?")
            .dataSource(dataSource)
            .passwordEncoder(bCryptPasswordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable().authorizeRequests().antMatchers("/api/**").fullyAuthenticated().and().httpBasic()
                .realmName(REALM).authenticationEntryPoint(getBasicAuthEntryPoint()).and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    }
}

The following RequestMapping does with @RolesAllowed but it works if I remove @RolesAllowed . I am getting the following error.

{
    "timestamp": 1510628906600,
    "status": 403,
    "error": "Forbidden",
    "exception": "org.springframework.security.access.AccessDeniedException",
    "message": "Access is denied",
    "path": "/api/user/admin"
} 

@RestController
@RequestMapping(value = "/api/user", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {

    @Autowired
    UserService us;

    @RolesAllowed(Role.SUPERADMIN)
    @RequestMapping(value = "/admin", method = RequestMethod.POST)
    public ResponseEntity<User> createAdmin(@RequestBody UserWrapper uw) {
       return new ResponseEntity<User>(uw.getUser(), HttpStatus.OK);
    }
}

I have tried to inspect the authenticated user with System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());

which returns

org.springframework.security.core.userdetails.User@ca3b1db5: Username: test@test.dk; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: SUPERADMIN

I have tried to remove @RolesAllowed and replaced the following line in the http.csrf().disable().authorizeRequests().antMatchers("/api/**").fullyAuthenticated().and().httpBasic()

with

http.csrf().disable().authorizeRequests().antMatchers("/api/**").hasAnyRole(String.format("%s,%s,%s", Role.SUPERADMIN, Role.ADMIN, Role.STAFF)).and().httpBasic()

This gives me http status 403

{
    "timestamp": 1510629419006,
    "status": 403,
    "error": "Forbidden",
    "message": "Access is denied",
    "path": "/api/user/admin"
}

This is my two tables in my database. A user can only have a single role.

CREATE TABLE `role` (
   `role_id` int(11) NOT NULL AUTO_INCREMENT,
   `role` varchar(255) DEFAULT NULL,
   `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   `updated` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (`role_id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

CREATE TABLE `user` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `email` varchar(255) NOT NULL,
   `password` varchar(255) NOT NULL,
   `first_name` varchar(255) NOT NULL,
   `last_name` varchar(255) DEFAULT NULL,
   `role_id` int(11) NOT NULL,
   `enabled` bit(1) NOT NULL DEFAULT b'0',
   `last_login` timestamp NULL DEFAULT NULL,
   `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   `updated` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (`id`),
   UNIQUE KEY `email_UNIQUE` (`email`),
 ) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8

So I found the solution to my problem, I had to add .rolePrefix("ROLE_"); to my configureGlobalSecurity method.

I am not sure why this is the solution. The default role prefix for AuthenticationManagerBuilder is an empty String according to according the documentation. So my best guess is that Spring default role prefix is "ROLE_".

If anyone knows the real explanation behind this madness please either post a comment or an answer. I would be very thanksful!

EDIT: @RolesAllowed default prefix is ROLE_ which is why you have to add ROLE_ prefix to spring security!

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
     public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
         auth.
            jdbcAuthentication()
            .usersByUsernameQuery(usersQuery)
            .authoritiesByUsernameQuery(rolesQuery)
            .dataSource(dataSource)
            .passwordEncoder(bCryptPasswordEncoder)
            //.rolePrefix fixed the problem
            .rolePrefix("ROLE_");           
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable().authorizeRequests().antMatchers("/api/**")
            .hasAnyRole(String.format("%s,%s,%s", Role.SUPERADMIN, Role.ADMIN, Role.STAFF)).and().httpBasic()
            .realmName(REALM).authenticationEntryPoint(getBasicAuthEntryPoint()).and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    }
}

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM