Las anotaciones de permisos SpringSecurity de SpringBoot realizan la autenticación de permisos en métodos de varias maneras

prefacio

Spring SecuritySoporte de control de permisos a nivel de método. Con base en este mecanismo, podemos agregar anotaciones de permisos a cualquier método en cualquier capa, y el método agregado a la anotación se protegerá automáticamente Spring Security, permitiendo el acceso solo a usuarios específicos, para lograr el propósito del control de permisos. la anotación de permiso existente Si no está satisfecho, también podemos personalizar

Inicio rápido

  1. Primero agregue la dependencia de seguridad de la siguiente manera
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. Luego cree una nueva clase de configuración de seguridad

Spring SecurityDe forma predeterminada, las anotaciones están deshabilitadas. Para habilitar las anotaciones, debe anotar la WebSecurityConfigurerAdapterclase heredada @EnableMethodSecurityy AuthenticationManagerdefinirla comoBean。

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

Vemos que @EnableGlobalMethodSecurityhay tres campos respectivamente prePostEnabled 、securedEnabled、jsr250Enabled, cada uno de los cuales codifica un soporte de anotación, y el predeterminado es false,true为开启. Luego, hablemos sobre el soporte de estas tres anotaciones generales una por una.

El efecto de prePostEnabled = true es habilitar las anotaciones @PreAuthorize y @PostAuthorize de Spring Security.

La función de secureEnabled = true es habilitar la anotación @Secured de Spring Security.

La función de jsr250Enabled = true es habilitar la anotación @RoleAllowed

Para un uso e integración más detallados, consulte mis dos artículos
Fácil de usar SpringBoot+SpringSecurity+JWT Combate de control de permisos RESTfulAPI real

Adquisición de autoridad de usuario de la interfaz central de Spring Security, principio de ejecución del proceso de autenticación

Establecer autenticación de autorización en el método

Anotaciones JSR-250

Cumplir con las anotaciones estándar JSR-250
Anotaciones principales

  1. @DenyAll
  2. @rolespermitidos
  3. @PermitAll

Aquí @DenyAlly @PermitAllcreo que sobra decirlo, representan rechazo y aprobación.

@RolesAllowedEjemplo de uso

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

Se puede acceder a los métodos que representan anotaciones siempre que tengan permisos de USUARIO, ADMINISTRADOR. El prefijo ROLE_ se puede omitir aquí y la autoridad real puede ser ROLE_ADMIN

En términos de función y uso, es @Securedexactamente lo mismo que

anotación secureEnabled

nota principal

@Asegurado

  1. Anotaciones para Spring Security @Secured. La anotación especifica la lista de funciones del método de acceso, y al menos una función se especifica en la lista

  2. @SecuredEspecifique la seguridad en el método, requiriendo roles/permisos, etc. Solo los usuarios con los roles/permisos correspondientes pueden llamar a estos métodos. Si alguien intenta llamar a un método, pero no tiene los roles/permisos requeridos, se generará una excepción de acceso denegado.

Por ejemplo:

@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")Indica que solo ROLE_VIEWERlos usuarios con el rol pueden acceder getUsername()al método.

@Secured({ "ROLE_DBA", "ROLE_ADMIN" })Indica que el usuario tiene " ROLE_DBA", "ROLE_ADMIN"cualquiera de los dos roles y puede acceder getUsername2al método.

Otro punto es que @Secured no admite expresiones Spring EL

anotación prePostEnabled

Después de activarlo, es bastante potente para admitir expresiones Spring EL. Se lanza una AccessDeniedException si no hay permiso para acceder al método.

nota principal

  1. @PreAuthorize-- Adecuado para validar la autorización antes de ingresar al método

  2. @PostAuthorize-- Comprobar el método de autorización antes de que se ejecute y puede afectar el valor de retorno del método de ejecución

3. @PostFilter--Ejecutar después de ejecutar el método, y aquí puede llamar al valor de retorno del método, luego filtrar o procesar o modificar el valor de retorno y regresar

  1. @PreFilter-- Ejecutar antes de que se ejecute el método, y aquí puede llamar a los parámetros del método y luego filtrar, procesar o modificar los valores de los parámetros

Uso de la anotación @PreAuthorize

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

@PreAuthorize("hasRole('ROLE_VIEWER')") es equivalente a @Secured("ROLE_VIEWER").

El mismo @Secured({“ROLE_VIEWER”,”ROLE_EDITOR”})también puede ser reemplazado por: @PreAuthorize(“hasRole(‘ROLE_VIEWER') or hasRole(‘ROLE_EDITOR')”).

Además, podemos usar expresiones en los parámetros del método:

Ejecutado antes de que se ejecute el método, aquí puede llamar a los parámetros del método, y también puede obtener el valor del parámetro. Aquí, se usa la función de reflejo del nombre del parámetro de JAVA8. Si no hay JAVA8, también puede usar Spring @P de Security para marcar parámetros, o utilice @Param de Spring Data para marcar los parámetros.

//无java8
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")
void changePassword(@P("userId") long userId ){
    
    }
//有java8
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")
void changePassword(long userId ){
    
    }

Esto significa que changePasswordantes de que se ejecute el método, se juzga si el valor del parámetro del método userId es igual al userId del usuario actual guardado en el principal, o si el usuario actual tiene la autoridad ROLE_ADMIN, y si uno de los dos coincidencias, se puede acceder al método.

@PostAuthorize uso de anotaciones

Después de ejecutar el método, se puede obtener el valor de retorno del método y se puede determinar el resultado final de la autorización de acuerdo con el método (ya sea para permitir el acceso o no):

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

loadUserDetailEn el código anterior, solo se permite el acceso cuando el nombre de usuario en el valor de retorno del método es el mismo que el nombre de usuario del usuario que ha iniciado sesión actualmente.

Tenga en cuenta que si EL es falso, entonces el método también se ha ejecutado y se puede revertir. La variable EL returnObject representa el objeto devuelto.

Se utilizan las anotaciones @PreFilter y @PostFilter

Spring Security proporciona una @PreFilteranotación para filtrar los parámetros entrantes:

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

Cuando el subelemento de los nombres de usuario sea diferente del nombre de usuario del usuario que ha iniciado sesión actualmente, consérvelo; cuando el subelemento de los nombres de usuario sea el mismo que el nombre de usuario del usuario que ha iniciado sesión actualmente, elimínelo. Por ejemplo, el nombre de usuario del usuario actual es zhangsan, y el valor de los nombres de usuario en este momento es {"zhangsan", "lisi", "wangwu"}, luego de filtrar por @PreFilter, el valor de los nombres de usuario realmente pasados ​​es{"lisi", "wangwu"}

Si el método de ejecución contiene varios parámetros de tipo Colección, filterObject no sabrá qué parámetro de Colección filtrar. En este punto, debe agregar el atributo filterTarget para especificar el nombre del parámetro específico:

@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(";"));
}

Del mismo modo, también podemos usar la Colección que @PostFilterse ha anotado 返回para filtrar:

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

En este punto, filterObject representa el valor devuelto. Si se sigue el código anterior, se realizará: elimine el subelemento en el valor de retorno que es el mismo que el nombre de usuario del usuario que ha iniciado sesión actualmente.

Meta anotaciones personalizadas

Si necesitamos usar la misma anotación de seguridad en varios métodos, podemos mejorar la mantenibilidad del proyecto creando meta-anotaciones.

Por ejemplo, cree las siguientes meta-anotaciones:

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

Luego puede agregar directamente la anotación al método correspondiente:

@IsViewer
public String getUsername4() {
    
    
    //...
}

En los proyectos de producción, dado que las metaanotaciones separan la lógica empresarial y el marco de seguridad, el uso de metaanotaciones es una muy buena opción.

Usar anotaciones de seguridad en las clases

Si todos los métodos de una clase se aplican con la misma anotación de seguridad, entonces la anotación de seguridad debe elevarse al nivel de clase en este momento:

@Service
@PreAuthorize("hasRole('ROLE_ADMIN')")
public class SystemService {
    
    
  
    public String getSystemYear(){
    
    
        //...
    }
  
    public String getSystemDate(){
    
    
        //...
    }
}

El código anterior se da cuenta de que se requiere el permiso ROLE_ADMIN para acceder a los métodos getSystemYear y getSystemDate.

Aplicar múltiples anotaciones de seguridad a los métodos

Cuando una anotación de seguridad no puede satisfacer nuestras necesidades, también se pueden aplicar múltiples anotaciones de seguridad:

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

En este momento, Spring Security ejecutará la política de seguridad de @PreAuthorize antes de ejecutar el método y ejecutará la política de seguridad de @PostAuthorize después de ejecutar el método.

Resumir

Basado en nuestra experiencia, aquí hay dos consejos:

  1. De forma predeterminada, el proxy Spring AOP implementa el uso de anotaciones de seguridad en los métodos, lo que significa que si llamamos al método 2 en la misma clase que usa anotaciones de seguridad en el método 1, las anotaciones de seguridad en el método 2 no serán válidas.

  2. El contexto de Spring Security está vinculado a subprocesos, lo que significa que el contexto de seguridad no se pasará a subprocesos secundarios.

public boolean isValidUsername4(String username) {
    
    
    // 以下的方法将会跳过安全认证
    this.getUsername();
    return true;
}

おすすめ

転載: blog.csdn.net/u011738045/article/details/120528417