编程知识 cdmana.com

The spring security permission annotation of springboot performs permission authentication on Methods in a variety of ways

Preface

Spring Security Support method level permission control . In this mechanism , We can add permission annotations to any method in any layer , The method of adding annotation will be automatically Spring Security Protect it , Allow only specific users to access , So as to achieve the purpose of permission control , Of course, if the existing permission annotations are not satisfied, we can also customize

Quick start

  1. First add security Depends on the following
<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-security</artifactId></dependency>

2. Then create a new security configuration class

Spring Security Annotation is disabled by default , To open the annotation , To inherit WebSecurityConfigurerAdapter Class addition @EnableMethodSecurity annotation , And in this class AuthenticationManager Defined as Bean.

@[email protected]@EnableGlobalMethodSecurity(  prePostEnabled = true,   securedEnabled = true,   jsr250Enabled = true)public class SecurityConfig extends WebSecurityConfigurerAdapter {    @Bean    @Override    public AuthenticationManager authenticationManagerBean() throws Exception {        return super.authenticationManagerBean();    }}

We see @EnableGlobalMethodSecurity There were prePostEnabled 、securedEnabled、jsr250Enabled Three fields , Each field code has an annotation support , The default is false,true For opening . So let's talk about the three general annotations one by one .

prePostEnabled = true The purpose of is to enable Spring Security Of @PreAuthorize as well as @PostAuthorize annotation .

securedEnabled = true The function of is to enable Spring Security Of @Secured annotation .

jsr250Enabled = true The function of is to enable @RoleAllowed annotation

For more details on using integration, please refer to my two articles Pick up SpringBoot+SpringSecurity+JWT real RESTfulAPI Authority control practice

Spring Security Core interface user permission acquisition , Implementation principle of authentication process

Set the authority authentication on the method

JSR-250 annotation

Yes JSR-250 Standard notes main notes

  1. @DenyAll
  2. @RolesAllowed
  3. @PermitAll

Inside this @DenyAll and @PermitAll I don't need to say more The representative rejected and adopted .

@RolesAllowed Examples of use

@RolesAllowed("ROLE_VIEWER")public String getUsername2() {    //...}@RolesAllowed({ "USER", "ADMIN" })public boolean isValidUsername2(String username) {    //...}

The method of representing a dimension as long as it has USER, ADMIN Any kind of permission can access . The prefix can be omitted here ROLE_, The actual authority may be ROLE_ADMIN

It is similar to... In function and use method @Secured Exactly the same

securedEnabled annotation

Main notes

@Secured

  1. Spring Security Of @Secured annotation . The annotation specifies a list of roles that access the method , Specify at least one role in the list

  2. @Secured Specify security on the method , requirement role / Authority, etc Only corresponding role / jurisdiction Users can call these methods . If someone tries to call a method , But don't have what you need role / jurisdiction , That will deny access and throw an exception .

such as :

@Secured("ROLE_VIEWER")public String getUsername() {    SecurityContext securityContext = SecurityContextHolder.getContext();    return securityContext.getAuthentication().getName();}@Secured({ "ROLE_DBA", "ROLE_ADMIN" })public String getUsername2() {    //...}

@Secured("ROLE_VIEWER") Means only have ROLE_VIEWER Users of roles , To be able to access getUsername() Method .

@Secured({ "ROLE_DBA", "ROLE_ADMIN" }) Indicates that the user owns "ROLE_DBA", "ROLE_ADMIN" Either of the two roles , You can visit getUsername2 Method .

The other thing is that @Secured, I won't support it Spring EL expression

prePostEnabled annotation

This supports Spring EL expression It's pretty good . If you don't have access to the method , Will throw out AccessDeniedException.

Main notes

  1. @PreAuthorize -- It's appropriate to verify authorization before entering the method

  2. @PostAuthorize -- After checking the authorization method, it is executed and can affect the return value of the execution method

  3. @PostFilter -- Execute after the method executes , And here you can call the return value of the method , Then filter or process or modify the return value and return

  4. @PreFilter -- Execute before the method executes , And here you can call the parameters of the method , Then filter or process or modify the parameter value

@PreAuthorize Annotations use

@PreAuthorize("hasRole('ROLE_VIEWER')")public String getUsernameInUpperCase() {    return getUsername().toUpperCase();}

@PreAuthorize("hasRole('ROLE_VIEWER')") amount to @Secured(“ROLE_VIEWER”).

alike @Secured({“ROLE_VIEWER”,”ROLE_EDITOR”}) It can also be replaced by :@PreAuthorize(“hasRole(‘ROLE_VIEWER') or hasRole(‘ROLE_EDITOR')”).

in addition to , We can also use expressions on method parameters :

Execute before the method executes , Here you can call the parameters of the method , You can also get the parameter value , Here use JAVA8 Parameter name reflection property , without JAVA8, Then you can also use Spring Secuirty Of @P Label parameters , Or use Spring Data Of @Param Label parameters .

// nothing [email protected]("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")void changePassword(@P("userId") long userId ){}// Yes [email protected]("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")void changePassword(long userId ){}

It's in changePassword Before method execution , Judge method parameters userId Is the value of principal Of the current user saved in userId, Or whether the current user has ROLE_ADMIN jurisdiction , One of the two , You can visit the Method .

@PostAuthorize Annotations use

After the method is executed, it can be executed , To get the return value of the method , And the final authorization result can be determined according to this method ( Whether access is allowed or not ):

@PostAuthorize  ("returnObject.username == authentication.principal.nickName")public CustomUser loadUserDetail(String username) {    return userRoleRepository.loadUserByUserName(username);}

In the above code , Only when the loadUserDetail Method username With the currently logged in user username Access to... Is only allowed when it is the same

Note that if EL by false, Then the method has been executed , It may roll back .EL Variable returnObject Represents the returned object .

@PreFilter as well as @PostFilter Annotations use

Spring Security Provides a @PreFilter Annotation to filter the passed in parameters :

@PreFilter("filterObject != authentication.principal.username")public String joinUsernames(List<String> usernames) {    return usernames.stream().collect(Collectors.joining(";"));}

When usernames The subkey in is different from the user name of the currently logged in user , The retention ; When usernames The subkey in is the same as the user name of the currently logged in user , The remove . For example, the user name of the current user is zhangsan, here usernames The value of is {"zhangsan", "lisi", "wangwu"}, Then Jing @PreFilter After filtration , The actual incoming usernames The value of is {"lisi", "wangwu"}

If the execution method contains more than one type, it is Collection Parameters of ,filterObject It's not clear which one is right Collection Parameters are filtered . here , You need to join filterTarget Property to specify the specific parameter name :

@PreFilter  (value = "filterObject != authentication.principal.username",  filterTarget = "usernames")public String joinUsernamesAndRoles(  List<String> usernames, List<String> roles) {    return usernames.stream().collect(Collectors.joining(";"))       + ":" + roles.stream().collect(Collectors.joining(";"));}

Similarly, we can also use @PostFilter Comments came return Of Collection To filter :

@PostFilter("filterObject != authentication.principal.username")public List<String> getAllUsernamesExceptCurrent() {    return userRoleRepository.getAllUsernames();}

here filterObject Represents the return value . If you follow the above code, you realize : Remove the subitem in the return value that is the same as the user name of the currently logged in user .

Custom meta annotation

If we need to use the same security annotation in multiple methods , The maintainability of the project can be improved by creating meta annotations .

For example, create the following meta annotation :

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@PreAuthorize("hasRole('ROLE_VIEWER')")public @interface IsViewer {}

You can then add the annotation directly to the corresponding method :

@IsViewerpublic String getUsername4() {    //...}

In production projects , Because meta annotations separate business logic from security framework , So using meta annotations is a very good choice .

Class

If we apply the same security annotation to all the methods in a class , At this point, the security annotation should be promoted to the class level :

@[email protected]("hasRole('ROLE_ADMIN')")public class SystemService {    public String getSystemYear(){        //...    }    public String getSystemDate(){        //...    }}

The above code implements : visit getSystemYear as well as getSystemDate All methods require ROLE_ADMIN jurisdiction .

Method

When a security annotation cannot meet our needs , You can also apply multiple security annotations :

@PreAuthorize("#username == authentication.principal.username")@PostAuthorize("returnObject.username == authentication.principal.nickName")public CustomUser securedLoadUserDetail(String username) {    return userRoleRepository.loadUserByUserName(username);}

here Spring Security Will execute... Before executing the method @PreAuthorize Security policy , Execute... After executing the method @PostAuthorize Security policy .

summary

Combined with our experience , Give the following two tips :

  1. By default , The use of security annotations in methods is by Spring AOP The agent implements , It means : If we're in a way 1 To call methods using security annotations in the same class 2, Then the method 2 The safety notes on the will become invalid .

  2. Spring Security Context is thread bound , It means : The security context will not be passed to the child thread .

public boolean isValidUsername4(String username) {    //  The following method will skip security authentication     this.getUsername();    return true;}

版权声明
本文为[kenx]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/10/20211002145637035n.html

Scroll to Top