[Explicación detallada de Spring Security (10)] Descripción general y detalles de uso de la gestión de autoridades

1. El concepto central de la autorización

En el blog anterior en la columna, el proceso de autenticación se describe en detalle. Una vez que sabemos que la autenticación es exitosa, la información del usuario actualmente conectado se guardará en el objeto. Hay un método en el objeto de autenticación para devolver el Authenticationpermiso getAuthorities(). información del usuario conectado actualmente. , es decir, el usuario actual tiene información de permisos. El valor de retorno de este método es Collection<? extends GrantedAuthority>que cuando se requiere un juicio de permiso, se llamará al método correspondiente para juzgar de acuerdo con la información de permiso devuelta por el conjunto. (Y cómo el contexto obtiene esta autenticación después de la autenticación también se explica en detalle en el blog anterior; obténgalo a través de la clase SecurityContextHolder)

inserte la descripción de la imagen aquíMire el marcador de posición del valor de retorno, ¿ GrantedAuthoritycómo debemos entender el valor de retorno? ¿Es un rol o un permiso?

Para la gestión de autorizaciones, generalmente usamos el RBACmodelo familiar, donde R puede llamarse Resourceso llamarse, Roleses decir, podemos referirnos a la autorización comoGestión de permisos basada en rolesyBasado en la gestión de derechos de recursos. Desde una perspectiva de diseño, los roles y los permisos son dos cosas completamente diferentes: los permisos son algunas operaciones específicas y los roles son una colección de ciertos permisos. Tales como: READ_BOOK y ROLE_ADMIN son completamente diferentes. Entonces, cuál es el valor de retorno depende de la situación del diseño comercial:

  • El diseño basado en permisos de roles es: 用户<=>角色<=>资源el retorno de la relación entre los tres es el del usuario 角色.
  • El diseño basado en permisos de recursos es: 用户<=>权限<=>资源el retorno de la relación entre los tres es el del usuario权限
  • El diseño basado en roles y permisos de recursos es: 用户<=>角色<=>权限<=>资源volver denominados colectivamente como usuarios 权限.

¿Por qué pueden denominarse colectivamente permisos? Debido a que no hay mucha diferencia entre roles y permisos a nivel de código, todos son permisos. En Spring Security en particular, los roles y permisos se manejan básicamente de la misma manera (ambos son cadenas). La única diferencia es que Spring Security agregará automáticamente un ROLE_prefijo a los roles en muchos casos, pero los permisos no se agregarán automáticamente (esto se deriva de la implementación interna de Spring Security).

2. Estrategia de gestión de autoridades

En el desarrollo real, la autenticación siempre se fija durante el proyecto, mientras que la gestión de permisos es generalmente variable y los desarrolladores deben comprenderla.

Hay dos tipos principales de estrategias de gestión de autoridad proporcionadas en Spring Security :

¿A qué recursos del sistema se puede acceder? <=> (http, URL, método

  • Gestión de derechos basada en filtros ( FilterSecurityInterceptor)

    • La gestión de autoridad basada en filtros se utiliza principalmente para interceptar solicitudes HTTP y, después de la intercepción, la verificación de autoridad se realiza de acuerdo con la dirección de solicitud HTTP.
  • Gestión de derechos basada en AOP ( MethodSecurityInterceptor)

    • La administración de permisos basada en AOP se usa principalmente para tratar problemas de permisos a nivel de método. Cuando es necesario llamar a un método, la operación se intercepta a través de AOP y luego se juzga si el usuario tiene la autoridad pertinente.

Expresiones de permiso (SpEL Spring EL)

Antes de explicar las dos estrategias de administración de permisos, expliquemos la expresión de permiso integrada de Spring Security (expresión SpEL). Se utiliza principalmente para configurar permisos, podemos configurar los permisos requeridos a través de expresiones de permiso en la URL solicitada o método de acceso (anotación). Las siguientes son las expresiones de permiso integradas de Spring Security:

inserte la descripción de la imagen aquí

expresión efecto
hasRole(Rol de cadena) Si el usuario actual tiene el rol especificado
hasAnyRole(Cadena…roles) Si el usuario actual tiene alguno de los roles especificados
hasAuthority(Autoridad de cadena) Si el usuario actual tiene los permisos especificados
hasAnyAuthority(String…autoridades) Si el usuario actual tiene alguno de los permisos especificados
permitirTodo() Permitir todas las solicitudes/llamadas
denyAll() Denegar todas las solicitudes/llamadas
esAnónimo() Si el usuario actual es un usuario anónimo
está autenticado() Si el usuario actual se ha autenticado con éxito
esRecuérdame() Si el usuario actual inicia sesión automáticamente a través de RememberMe
está completamente autenticado () Si el usuario actual no es anónimo ni inició sesión automáticamente a través de RememberMe
hasPermission(Objetivo de objeto,Permiso de objeto) Si el usuario actual tiene los permisos especificados para el objetivo especificado
hasPermission(Object targetId,String targetType,Permiso de objeto) Si el usuario actual tiene los permisos especificados para el objetivo especificado
obtener autenticación () Obtener el objeto de autenticación
autenticación Este es el objeto de autenticación obtenido del SecurityContext, que ya está autenticado
principal Representa el principal de inicio de sesión actual Principal

En términos generales, las expresiones de permiso integradas proporcionadas por Spring Security son suficientes.

1. Gestión de autoridad basada en URL (filtro)

La gestión de autoridad basada en la dirección URL se realiza principalmente a través del filtro FilterSecurityInterceptor. Si el desarrollador configura la administración de permisos en función de la dirección URL, FilterSecurityInterceptor se agregará automáticamente a la cadena de filtros de Spring Security, y la cadena de filtros interceptará la solicitud y luego se analizará si el usuario actual tiene el permiso requerido. para la solicitud, si no está presente, se lanza una excepción.

uso básico

(Al crear el proyecto SpringBoot, accidentalmente creé la versión 6.1. No quiero cambiarla. La siguiente prueba se basa en la última versión. Se han habilitado algunos métodos. La siguiente es la última versión del código y la versión 6.1 SpringSecurity no tiene @Configuration en la meta-anotación de la anotación @EnableWebSecurity, así que recuerde agregarlo afuera, esto es un hoyo).

clase de configuración

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    

    @Bean
    public UserDetailsService userDetailsService(){
    
    
        return new InMemoryUserDetailsManager(
                User.withUsername("root").password("{noop}123").roles("ADMIN","USER").authorities("READ_INFO").build(),
                User.withUsername("admin").password("{noop}123").roles("USER").build(),
                User.withUsername("myz").password("{noop}123").authorities("READ_INFO").build()
        );
    }

    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http,@Autowired UserDetailsService userDetailsService) throws Exception {
    
    
        AuthenticationManagerBuilder builder = http.getSharedObject(AuthenticationManagerBuilder.class);
        builder.userDetailsService(userDetailsService);
        return builder.build();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    
    
        return http.authorizeRequests()
                .requestMatchers("/user").hasRole("USER")
                .requestMatchers("/getInfo").hasAuthority("READ_INFO")
                .requestMatchers("/admin").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
                .formLogin(configurer->{
    
    })
                .csrf(configurer->configurer.disable())
                .build();
    }

}

controlador de prueba

@RestController
public class TestController {
    
    

    @GetMapping("/user")
    public String user(){
    
    
        return "user ok!";
    }

    @GetMapping("/admin")
    public String admin(){
    
    
        return "admin ok!";
    }

    @GetMapping("/getInfo")
    public String getInfo(){
    
    
        return "info ok!";
    }

}

Resultados de la prueba: los usuarios que iniciaron sesión con myz/123 solo pueden acceder /getInfo; los usuarios que iniciaron sesión con admin/123 solo pueden acceder /user; los usuarios que iniciaron sesión con root/123 pueden acceder a las tres rutas de recursos de prueba.

2. Gestión de derechos basada en métodos (AOP)

La gestión de derechos basada en métodos se implementa principalmente a través de AOP, y Spring Security MethodSecurityInterceptorproporciona implementaciones relacionadas a través de . La diferencia es que en la URL, FilterSecurityInterceptoreste tipo de solicitud se procesa previamente, mientras que MethodSecurityInterceptor también puede realizar un procesamiento posterior además del procesamiento previo ( equivalente a un aspecto de envoltura ). El procesamiento previo es para determinar si tiene los permisos correspondientes antes de la solicitud, y el procesamiento posterior es para realizar un filtrado secundario en los resultados de ejecución del método.El preprocesamiento y el posprocesamiento corresponden a diferentes clases de implementación.

@EnableGlobalMethodSecurity

La anotación EnableGlobalMethodSecurity se utiliza paraAnotación de permiso abiertoSí, la anotación está habilitada para usarse en el código del proyecto. El uso es el siguiente:

@EnableWebSecurity
@EnableGlobalMethodSecurity(
        prePostEnabled = true,
        securedEnabled = true,
        jsr250Enabled = true
)
public class SecurityConfig {
    
    
}
  • perPostEnabled: abra las cuatro anotaciones de permisos proporcionadas por Spring Security, @PostAuthorize, @PostFilter, @PreAuthorize y @PreFilter.
  • securedEnabled: habilite la anotación @Secured proporcionada por Spring Security,Esta anotación no admite expresiones de permiso
  • jsr250Enabled: Abra las anotaciones proporcionadas por JSR-250, principalmente @DenyAll, @PermitAll, @RolesAll.Además, estas anotaciones no admiten expresiones de permiso.

Los significados de las anotaciones mencionadas anteriormente se muestran en la siguiente tabla:

anotación significado
@PostAutorizar La verificación de permisos se realiza después de que se ejecuta el método de destino
@postfiltro Después de que el método de destino se ejecuta enresultado devueltoFiltrar
@preautorizar La verificación de permisos se realiza antes de que se ejecute el método de destino
@prefiltro Antes de que el método de destino ejecute elparámetros del métodoFiltrar
@Asegurado Los métodos de destino de acceso deben tener roles correspondientes
@DenyAll denegar todo acceso
@PermitAll permitir todo el acceso
@rolespermitidos El método de destino de acceso debe tener el rol correspondiente

Estas anotaciones relacionadas con la administración de permisos basada en métodos generalmente son suficientes para establecerse prePostEnabled=true, es decir, las primeras cuatro anotaciones en la tabla de versiones.

uso básico

Configuración de seguridad

@EnableWebSecurity
@EnableGlobalMethodSecurity(
        prePostEnabled = true,
        securedEnabled = true,
        jsr250Enabled = true
)
public class SecurityConfig {
    
    

    @Bean
    public UserDetailsService userDetailsService(){
    
    
        return new InMemoryUserDetailsManager(
                User.withUsername("root")
                        .password("{noop}123")
                        // .authorities("READ_INFO")
                        .roles("ADMIN","USER")
                        .build(),
                User.withUsername("admin").password("{noop}123").roles("USER").build(),
                User.withUsername("myz").password("{noop}123").authorities("READ_INFO").build()
        );
    }

    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
    
    
        AuthenticationManagerBuilder builder = http.getSharedObject(AuthenticationManagerBuilder.class);
        builder.userDetailsService(userDetailsService());
        return builder.build();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    
    
        return http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .csrf(configurer->configurer.disable())
                .build();
    }


}

AuthorizeMethodController

@RestController
@RequestMapping("/hello")
public class AuthorizeMethodController {
    
    

    @GetMapping("/getInfo")
    public Object getInfo(){
    
    
        System.out.println(SecurityContextHolder.getContext().getAuthentication().getName());
        return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }


    @PreAuthorize("hasRole('ADMIN') and authentication.name=='root'")
    @GetMapping
    public String hello(){
    
    
        return "hello";
    }

    @PreAuthorize("authentication.name==#name")
    @GetMapping("/name")
    public String hello(String name){
    
    
        return "hello:" + name;
    }

    @PreFilter(value="filterObject.id%2!=0",filterTarget = "users")// filterTarget 必须是 数组 集合类型
    @PostMapping("/users")
    public void addUsers(@RequestBody List<User> users){
    
    
        System.out.println("users = " + users);
    }

    @PostAuthorize("returnObject.id==1")
    @GetMapping("/userId")
    public User getUserById(Integer id){
    
    
        return new User("myz",id);
    }

    @PostFilter("filterObject.id%2==0")// 用来对方法的返回值进行过滤,filterObject也是需要是集合或者是数组才行
    @GetMapping("/lists")
    public List<User> getAll(){
    
    
        List<User> users = new ArrayList<>();
        for(int i=0;i<10;++i){
    
    
            users.add(new User("myz:" + i, i));
        }
        return users;
    }



    @Secured("ROLE_USER")// 只能判断角色
    @GetMapping("/secured")
    public User getUserByUsername(){
    
    
        return new User("secured",999);
    }

    @Secured({
    
    "ROLE_USER","ROLE_ADMIN"})
    @GetMapping("/username")
    public User getUsername(String username){
    
    
        return new User(username,11111);
    }

}

Usuario personalizado

@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
    
    
    private String name;
    private Integer id;

}

Resultado de la prueba: ligeramente ~

3. El problema de la versión de la gestión de autoridad

Nota: En Spring Security versión 5.7.8, la adición de permisos de la clase de implementación UserDetails User proporcionada internamente en Spring Security depende de esto último, es decir, cuando se usa el método o para agregar permisos de objetos de usuario, los permisos reales del rolesobjeto authoritiesUser dependen del método de uso posterior, que se deriva de la siguiente implementación interna:

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
Como se puede ver en el código fuente, el objeto de colección de autoridades de atributos internos se vuelve a instanciar y se asigna cada vez que se llama al método de roles o autoridades.

En la última versión 6.1.0, las autoridades de atributos internos son un objeto de colección vacío, y cuando se utilizan los métodos de roles y autoridades, los elementos se agregan a la colección de autoridades internas. se puede utilizar simultáneamente.

Pero el editor piensa que en la práctica, haremos coincidir nuestra propia clase de implementación UserDetails e implementaremos métodos personalizados, por lo que estos problemas no se encontrarán hasta cierto punto. Pero cuando use InMemoryUserDetailsManager para una prueba, debe prestar atención a esto. Después de todo, la información persistente del usuario es más significativa.

Supongo que te gusta

Origin blog.csdn.net/qq_63691275/article/details/131191590
Recomendado
Clasificación