Spring Security, Method Security annotation (@Secu

2020-05-23 02:18发布

I am trying to set up a method security annotation using @Secured("ADMIN") (without any XML, only java config, Spring Boot). But access via roles does not work.

Security Config:

@Configuration
@EnableWebSecurity
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter{

.....

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/api/**").fullyAuthenticated().and()
                .addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

.....

}

I want restrict access to the method of the controller:

@RestController
@RequestMapping("/api/groups")
public class GroupController {

    @Autowired
    private GroupService groupService;

    @Secured("ADMIN")
    @RequestMapping
    public List<Group> list() {
        return groupService.findAll();
    }

}

Restrict access by the url is working, with:

.antMatchers("/api/**").hasAuthority("ADMIN")

Maybe I forgot to specify that I want restrict by roles?

UPD: By the rules, At what layer must be @PreAuthorize("hasRole('ADMIN')") in Controller layer or in Service layer?

7条回答
萌系小妹纸
2楼-- · 2020-05-23 03:04

This issue was solved.

I add @EnableGlobalMethodSecurity(prePostEnabled = true)

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter{
}

And in controller i changed @Secured("ADMIN") to @PreAuthorize("hasRole('ADMIN')")

查看更多
兄弟一词,经得起流年.
3楼-- · 2020-05-23 03:15

I want to share my decision, may be it will be helpful.

Used spring mvc + spring security, version 4.2.9.RELEASE

For example, i have a Service with method annotated @Secured

@Secured("ACTION_USER_LIST_VIEW")
List<User> getUsersList();

But, it didn't work, because GlobalMethodSecurityConfiguration has inside method.

protected AccessDecisionManager accessDecisionManager()

in which initialized the new RoleVoter() with default rolePrefix = "ROLE_"; (this makes it impossible to use beans to set your rolePrefix) that give to us not working annotations, because RoleVoter expects annotation value which starts with 'ROLE_'

For resolving this problem i override GlobalMethodSecurityConfiguration like this

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class AppMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
    @Override
    protected AccessDecisionManager accessDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
        ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
        expressionAdvice.setExpressionHandler(getExpressionHandler());
        decisionVoters.add(getRoleVoter());
        decisionVoters.add(new AuthenticatedVoter());
        return new AffirmativeBased(decisionVoters);
    }

    private RoleVoter getRoleVoter() {
        RoleVoter e = new RoleVoter();
        e.setRolePrefix("");
        return e;
    }
}
查看更多
聊天终结者
4楼-- · 2020-05-23 03:15

Maybe you should register your AppSecurityConfiguration to same context as WebMvcConfig (that extends WebMvcConfigurerAdapter).

AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();      
mvcContext.register(WebMvcConfig.class, SecurityConfig.class);
查看更多
三岁会撩人
5楼-- · 2020-05-23 03:16

You need to use @secured(ROLE_ADMIN) instead of @secured(ADMIN). You are required to write "ROLE_" infront of your role name. Please find the example mentioned below which is making sure only a user with Admin role can access list() method.

@RestController
@RequestMapping("/api/groups")
public class GroupController {

    @Autowired
    private GroupService groupService;

    @Secured("ROLE_ADMIN")
    @RequestMapping
    public List<Group> list() {
        return groupService.findAll();
    }

}
查看更多
We Are One
6楼-- · 2020-05-23 03:17

There may be many reasons for which method security on a controller does not work.

First because it is never cited as example in Spring Security manual ... joking but it may be tricky to take Spring tools where they do not want to go.

More seriously, you should enable method security as already said by @Mudassar. The manual says :

We can enable annotation-based security using the @EnableGlobalMethodSecurity annotation on any @Configuration instance. For example, the following would enable Spring Security’s @Secured annotation.

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
   // ...
}

Note that Mudassar's answer is correct till here.

But method security is based on AOP, which by default uses JDK proxying on interfaces. That's the reason why all examples applies method security on the service layer, because the service classes are normally injected in controllers as interfaces.

You can of course use it on controller layer, but :

  • either all your controllers implement interfaces for you all @Secured annotated methods
  • or you must switch to class proxying

The rule that I try to follow is :

  • if I want to secure an URL, I stick to HTTPSecurity
  • if I need to allow finer grained access, I add security at service layer
查看更多
Anthone
7楼-- · 2020-05-23 03:20

Kindly add this

@EnableGlobalMethodSecurity(securedEnabled = true)

This element is used to enable annotation-based security in your application (by setting the appropriate attributes on the element), and also to group together security pointcut declarations which will be applied across your entire application context specifically for @Secured. Hence your code should look like this

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter{..
查看更多
登录 后发表回答