Autenticación de usuario de Spring Security

Tabla de contenido

1. Autenticación de usuario

1. Análisis de necesidades

2. Conéctese a la base de datos del centro de usuarios

1. Conéctese a la autenticación de la base de datos

2. Ampliar la información de identidad del usuario.

¿Cómo ampliar la información de identidad del usuario de Spring Security?

3. El servicio de recursos obtiene la identidad del usuario.


1. Autenticación de usuario

1.  Análisis de requisitos

Hasta ahora hemos entendido el proceso de uso de Spring Security para autenticación y autorización. Esta sección implementa la función de autenticación de usuario.

En la actualidad, los principales sitios web tienen muchos métodos de autenticación: autenticación de contraseña de cuenta, autenticación de código de verificación de teléfono móvil, inicio de sesión con código de escaneo, etc.

Si no lo entiende, puede leerlo aquí: Proceso de autenticación OAuth2_blog-CSDN de Blog de Relievedz 

2. Conéctese a la base de datos del centro de usuarios

1.Conéctese  a la autenticación de la base de datos

El proceso de autenticación basado ha sido probado y aprobado durante la investigación de Spring Security. Hasta ahora, el proceso de autenticación del usuario es el siguiente:

 

La información del usuario requerida para la autenticación se almacena en la base de datos del centro de usuarios. Ahora es necesario conectar el servicio de autenticación a la base de datos para consultar la información del usuario.

En el proceso de estudio de Spring Security, la información del usuario está codificada de la siguiente manera:

//Configurar el servicio de información del usuario 
@Bean 
public UserDetailsService userDetailsService() { 
    //Configure la información del usuario aquí, use temporalmente este método para almacenar usuarios en la memoria 
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); 
    manager.createUser(User.withUsername(" zhangsan") .contraseña("123").authorities("p1").build()); 
    manager.createUser(User.withUsername("lisi").contraseña("456").authorities("p2"). build() ); 
    administrador de devoluciones; 
}

Necesitamos conectarnos a la base de datos del centro de usuarios en el servicio de autenticación para consultar la información del usuario.

¿Cómo utilizar Spring Security para conectarse a la autenticación de la base de datos?

Al aprender el principio de funcionamiento de Spring Security, hubo un diagrama de flujo de ejecución, como se muestra a continuación:

El usuario envía la cuenta y la contraseña y DaoAuthenticationProvider llama al método loadUserByUsername() de UserDetailsService para obtener la información del usuario UserDetails.

Consulte el código fuente de DaoAuthenticationProvider de la siguiente manera:

UserDetailsService es una interfaz, de la siguiente manera: este código es el código fuente del marco y no es necesario que lo escriba usted mismo.

paquete org.springframework.security.core.userdetails; 

interfaz pública UserDetailsService { 
    UserDetails loadUserByUsername (String var1) lanza UsernameNotFoundException; 
}

 UserDetails es la interfaz de información del usuario:  este código es el código fuente del marco y no es necesario que lo escriba usted mismo.

interfaz pública UserDetails extiende Serializable { 
    Colección<? extiende GrantedAuthority> getAuthorities(); 

    Cadena obtenerContraseña(); 

    Cadena getUsername(); 

    booleano isAccountNonExpired(); 

    booleano isAccountNonLocked(); 

    booleano isCredentialsNonExpired(); 

    booleano está habilitado(); 
}

 

Solo necesitamos implementar la interfaz UserDetailsService para consultar la base de datos para obtener información del usuario y devolver información del usuario del tipo UserDetails.

Primero, proteja el UserDetailsService definido originalmente. : Este es el código que escribí

    //Configurar el servicio de información del usuario 
// @Bean 
// public UserDetailsService userDetailsService() { 
// //Configure la información del usuario aquí, use temporalmente este método para almacenar usuarios en la memoria 
// InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); // 
manager. createUser(User.withUsername("zhangsan").password("123").authorities("p1").build()); // manager.createUser( 
User.withUsername("lisi").password( "456" ).authorities("p2").build()); 
// administrador de retorno; 
// }

Personalice el UserDetailsService a continuación   y escríbalo usted mismo

paquete com.xuecheng.ucenter.service.impl; 

importar com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 
importar com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; 
importar com.xuecheng.ucenter.mapper.XcUserMapper; 
importar com.xuecheng.ucenter.model.po.XcUser; 
importar lombok.extern.slf4j.Slf4j; 
importar org.springframework.beans.factory.annotation.Autowired; 
importar org.springframework.security.core.userdetails.User; 
importar org.springframework.security.core.userdetails.UserDetails; 
importar org.springframework.security.core.userdetails.UserDetailsService; 
importar org.springframework.security.core.userdetails.UsernameNotFoundException;
importar org.springframework.stereotype.Component; 

/** 
 * @programa: xuecheng-plus-project148
 * @description: la interfaz consulta la base de datos para obtener información del usuario y devuelve la información del usuario del tipo UserDetails. 
 * @author: Mr.Zhang 
 * @create: 2023-03-17 08:52 
 **/ 
@Slf4j 
@Component 
public class UserServiceImpl implements UserDetailsService { 

    @Autowired 
    XcUserMapper userMapper; 

    /** 
     * Consultar información del usuario según el número de cuenta 
     * @ param s Cuenta 
     * @return 
     * @throws UsernameNotFoundException 
     */ 
    @Override 
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { 
        //Cuenta 
        String nombre de usuario = s; 
        //Consulta la base de datos según la cuenta de nombre de usuario
        XcUser xcUser = userMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, nombre de usuario)); 

        //Si el usuario no existe, simplemente devuelve nulo y el marco de seguridad de Spring arroja una excepción 
        si el usuario no existe (xcUser==null){ 
            return null; 
        } 
        // Si el usuario obtiene la contraseña correcta, se encapsulará en un objeto UserDetails y se devolverá al marco de seguridad de Spring, y el marco comparará la contraseña String 
        contraseña = xcUser.getPassword(); 
        //Autoridad del usuario, si no se informa No se puede pasar una colección null GrantedAuthority 
        String[] Authority= {"test"}; 
        //Cree un objeto UserDetails y la información de autoridad se agregará a UserDetails cuando se implementa la función de autorización 
        userDetails = User.withUsername(username).password(contraseña).authorities(authorities).build(); 

        return userDetails; 
    } 
}

En este punto, debemos tener claro cómo se ejecuta el marco después de llamar al método loadUserByUsername() para obtener la información del usuario, como se muestra en la siguiente figura:

 

La contraseña en la base de datos está cifrada y la contraseña ingresada por el usuario está en texto sin formato. Necesitamos modificar el formateador de contraseñas PasswordEncoder. Se utilizó el NoOpPasswordEncoder original, que compara contraseñas en texto sin formato. Ahora lo cambiamos a BCryptPasswordEncoder, que compara la contraseña ingresada por el usuario. La contraseña se codifica en formato BCrypt y se compara con la contraseña en la base de datos.

como sigue:

    @Bean 
    public PasswordEncoder contraseñaEncoder() { 
// //La contraseña está en texto claro 
// return NoOpPasswordEncoder.getInstance(); 
        return new BCryptPasswordEncoder(); 
    }

Probamos BCryptPasswordEncoder a través del código de prueba, de la siguiente manera:

//Comparar contraseñas
public static void main(String[] args) { 
    String contraseña = "111111"; 
    BCryptPasswordEncoder contraseñaEncoder = new BCryptPasswordEncoder(); 

    for (int i = 0; i < 5; i++) { 
        // generar contraseña 
        String encode = contraseñaEncoder.encode (contraseña); 
        System.out.println(encode); 
        //Verifique la contraseña, el parámetro 1 es el texto sin formato de entrada, el parámetro 2 es la cadena cifrada de la contraseña correcta 
        coincidencias booleanas = contraseñaEncoder.matches(contraseña, codificar); 
        System .out .println(coincidencias); 
    } 
    coincidencias booleanas = contraseñaEncoder.matches("1111", "$2a$10$Q0ItVMXc/VwrlRE7NGBFtetut6o7vxpSjQDcKvtakIrCnxOWf.LV."); 
    System.out.println(coincidencias); 
}

Modifique la contraseña en la base de datos al formato Bcrypt y registre la contraseña en texto sin formato, que será necesaria cuando solicite un token más adelante.

Dado que cambiar el método de codificación de la contraseña también requiere cambiar la clave del cliente al formato Bcrypt.

// Servicio de detalles del cliente 
    @Override 
    public void configure(ClientDetailsServiceConfigurer clientes) 
            lanza una excepción { 
        client.inMemory()// Usar almacenamiento en memoria.withClient 
                ("XcWebApp")// client_id 
// .secret("XcWebApp")/ / Client secret.secret 
                (new BCryptPasswordEncoder().encode("XcWebApp"))//Client secret.resourceIds 
                ("xuecheng-plus")//Resource list.authorizedGrantTypes 
                ("authorization_code", "password", "client_credentials", " implícito", "refresh_token")// Tipos de autorización permitidos por este cliente código_autorización, contraseña, token_actualización, implícito, credenciales_cliente 
                .ámbitos("todos")// Ámbito de autorización permitido 
                . autoApprove(false)//false salta a la página de autorización
                //La dirección de redireccionamiento para que el cliente reciba el
                .redirectUris("http://www.xuecheng-plus.com") 
        ; 
    }

Ahora reinicie el servicio de autenticación.

Utilice httpclient para realizar las pruebas siguientes:

### 密码模式
POST { 
   
   {auth_host}}/auth/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=password&username=stu2&password=111111

Introducción de parámetros:

### Modo de contraseña 
POST { 
  
  {número de puerto del servidor}}/auth/oauth/token? ruta client_id=XcWebApp&client_secret=XcWebApp&grant_type=password&username=t1(mi nombre de usuario de base de datos)&password=111111(contraseña de base de datos)

Ingrese el número de cuenta y la contraseña correctos y la aplicación del token se realizará correctamente.

Si ingresa una contraseña incorrecta, aparecerá un mensaje de error:

{   "error": "invalid_grant",   "error_description": "Nombre de usuario o contraseña incorrectos" }


Si ingresa el número de cuenta incorrecto, aparecerá un mensaje de error:

{   "error": "no autorizado",   "error_description": "UserDetailsService devolvió un valor nulo, lo cual es una infracción del contrato de interfaz" }


 Ingresado correctamente:

2. Ampliar la información de identidad del usuario.

La tabla de usuarios almacena el número de cuenta del usuario, el número de teléfono móvil, el correo electrónico, el apodo, el qq y otra información, y la interfaz UserDetails solo devuelve el nombre de usuario, la contraseña y otra información, de la siguiente manera:

interfaz pública UserDetails extiende Serializable { 
    Colección<? extiende GrantedAuthority> getAuthorities(); 

    Cadena obtenerContraseña(); 

    Cadena getUsername(); 

    booleano isAccountNonExpired(); 

    booleano isAccountNonLocked(); 

    booleano isCredentialsNonExpired(); 

    booleano está habilitado(); 
}

Necesitamos expandir la información de identidad del usuario y almacenar el apodo, avatar, QQ y otra información del usuario en el token jwt.

¿ Cómo ampliar la información de identidad del usuario de Spring Security ?

Durante la fase de autenticación, DaoAuthenticationProvider llamará a UserDetailService para consultar la información del usuario, donde se puede obtener la información completa del usuario. Dado que la información de identidad del usuario en el token JWT proviene de UserDetails, UserDetails solo define el nombre de usuario como la información de identidad del usuario. Aquí hay dos ideas: la primera es extender UserDetails para incluir más atributos personalizados, y la segunda también es El contenido del nombre de usuario se puede ampliar, como almacenar el contenido de datos json como el contenido del nombre de usuario. En comparación, la opción dos es relativamente simple y no requiere destruir la estructura de UserDetails. Adoptamos la opción dos.

Modifique UserServiceImpl de la siguiente manera:

paquete com.xuecheng.ucenter.service.impl; 

importar com.alibaba.fastjson.JSON; 
importar com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 
importar com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; 
importar com.xuecheng.ucenter.mapper.XcUserMapper; 
importar com.xuecheng.ucenter.model.po.XcUser; 
importar lombok.extern.slf4j.Slf4j; 
importar org.springframework.beans.factory.annotation.Autowired; 
importar org.springframework.security.core.userdetails.User; 
importar org.springframework.security.core.userdetails.UserDetails; 
importar org.springframework.security.core.userdetails.UserDetailsService; 
importar org.springframework.security.core.userdetails.UsernameNotFoundException;
importar org.springframework.stereotype.Component; 

/** 
 * @program: xuecheng-plus-project148 
 * @description: la interfaz consulta la base de datos para obtener información del usuario y devuelve información del usuario de tipo UserDetails. 
 * @author: Mr.Zhang 
 * @create: 2023-03-17 08:52 
 **/ 
@Slf4j 
@Component 
public class UserServiceImpl implements UserDetailsService { 

    @Autowired 
    XcUserMapper userMapper; 

    /** 
     * Consultar información del usuario según el número de cuenta 
     * @ param s Cuenta 
     * @return 
     * @throws UsernameNotFoundException 
     */ 
    @Override 
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { 
        //Cuenta 
        String nombre de usuario = s; 
        // Consulta la base de datos según la cuenta de nombre de usuario 
        XcUser xcUser = userMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, nombre de usuario)); 

        //Si el usuario no existe, simplemente devuelve null, spring marco de seguridad Se lanza una excepción si el usuario no existe 
        if (xcUser==null){ 
            return null; 
        } 
        //Si se descubre que el usuario obtuvo la contraseña correcta, se encapsulará en un objeto UserDetails y se devolverá a Spring marco de seguridad, y el marco comparará la contraseña 
        String contraseña = xcUser.getPassword(); 
        // //Autoridad del usuario, si no se informa No se puede pasar una colección nula GrantedAuthority 
        String[] Authority= {"test"}; 
        xcUser.setPassword( null); 
        //Crear objeto UserDetails, información de autoridad al implementar la función de autorización, agregar 
        //Convertir información de usuario a json en UserDetail 
        String userJson = JSON.toJSONString(xcUser);
        UserDetails userDetails = Usuario.withUsername(userJson).contraseña(contraseña).autoridades(autoridades).build(); 

        devolver detalles del usuario; 
    } 
}

Reinicie el servicio de autenticación, regenere el token y la generación se realizará correctamente.

Podemos usar check_token para consultar el contenido de jwt

###Verifique el token jwt 
POST { 
   
   {auth_host}}/auth/oauth/check_token?token=

Un ejemplo de respuesta es el siguiente,

{   "aud": [     "xuecheng-plus"   ],   "user_name": "{\"createTime\":\"2022-09-28T08:32:03\",\"id\":\"51\" ,\"nombre\":\"学生2\",\"sexo\":\"1\",\"estado\":\"1\",\"nombre de usuario\":\"stu2\", \"utype\":\"101001\"}",   "scope": [     "all"   ],   "active": true,   "exp": 1679025018,   "authorities": [     "test"   ],   "jti": "14c4085b-7787-4ce8-8c24-90f1fc80df34",   "client_id": "XcWebApp" }














user_name almacena el formato json de la información del usuario. En el servicio de recursos, el contenido del formato json se puede extraer y convertir en un objeto de usuario para su uso.

3. El servicio de recursos obtiene la identidad del usuario.

Escriba una clase de herramienta a continuación para usar en cada microservicio para obtener el objeto del usuario actualmente conectado.

paquete com.xuecheng.content.util; 

importar com.alibaba.fastjson.JSON; 
importar lombok.Datos; 
importar lombok.extern.slf4j.Slf4j; 
importar org.springframework.security.core.context.SecurityContextHolder; 

importar java.io.Serializable; 
importar java.time.LocalDateTime; 


/** 
 * @program: xuecheng-plus-project148 
 * @description: 获取当前用户身份工具类
 * @autor: Mr.Zhang 
 * @create: 2023-03-17 10:05 
 **/ 
@Slf4j 
public class SecurityUtil { 

    público estático XcUser getUser() { 
        intentar { 
            Objeto principalObj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            if (principalObj instancia de String) {  
                //Obtener información de identidad del usuario
                String principal = principalObj.toString(); 
                //Convierte json en objeto 
                XcUser usuario = JSON.parseObject(principal, XcUser.class); 
                return usuario; 
            } 
        } catch (Exception e) { 
            log. error ("Error al obtener la identidad del usuario actualmente conectado: {}", e.getMessage()); 
            e.printStackTrace(); 
        } 

        return null; 
    } 


    @Data 
    public static class XcUser implements Serializable { 

        private static final long serialVersionUID = 1L; 

        ID de cadena privada; 

        nombre de usuario de cadena privada;

        contraseña de cadena privada; 
 
        sal de cadena privada;

        nombre de cadena privada; 
        apodo de cadena privada; 
        cadena privada wxUnionid; 
        ID de empresa de cadena privada; 
        /** 
         * 头像
         */ 
        foto de usuario de cadena privada; 

        tipo de cadena privada; 

        cumpleaños privado LocalDateTime; 

        sexo privado en cuerdas; 

        correo electrónico de cadena privada; 

        teléfono móvil String privado; 

        Cadena privada qq; 

        /** 
         * 用户状态
         */ 
        estado de cadena privada; 

        privado LocalDateTime createTime; 

        hora de actualización local fecha hora privada; 


    } 


}

A continuación, pruebe esta clase de herramienta en el servicio de gestión de contenidos, tomando como ejemplo la interfaz de consulta de información del curso:

  @ApiOperation("Consultar cursos según id") 
    @GetMapping("/course/{courseId}") 
    public CourseBaseInfoDto getCourseBaseById(@PathVariable Long courseId){ 
        //Obtener la identidad del usuario actual 
// Objeto principal = SecurityContextHolder.getContext (). getAuthentication().getPrincipal(); 
// System.out.println("Authentication"+principal); 
        SecurityUtil.XcUser usuario = SecurityUtil.getUser(); 
        System.out.println(user.getUsername()); 
        CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId); 
        devolver courseBaseInfo; 
    }

Reinicie el servicio de gestión de contenidos:

1. Inicie el servicio de autenticación, la puerta de enlace y el servicio de gestión de contenidos.

2. Generar un nuevo token

3. Traiga el token para acceder a la interfaz de consulta del curso del servicio de gestión de contenidos.

 

Supongo que te gusta

Origin blog.csdn.net/Relievedz/article/details/129611666
Recomendado
Clasificación