Notas en línea de Xuecheng + Pisar el pozo (11): Introducción a la autenticación y autorización, autenticación de puerta de enlace, SpringSecurity + JWT + OAuth2

navegación:

[Notas de Dark Horse Java + Resumen de Stepping Pit] JavaSE+JavaWeb+SSM+SpringBoot+Riji Takeaway+SpringCloud+Dark Horse Tourism+Grain Mall+Xuecheng Online+Preguntas de la entrevista de Nioke_notas de Java Dark Horse

 Tabla de contenido

1 [Módulo de autenticación] Análisis de requisitos

1.1 ¿Qué es la autenticación y la autorización?

1.2 Proceso de Negocio

1.2.1 Autenticación unificada

1.2.2 Inicio de sesión único SSO

1.2.3 Certificación de terceros

2 Marco de autorización y autenticación de Spring Security

2.1 Introducción a la seguridad de Spring

2.2 Primeros pasos con la autenticación y autorización

2.2.1 Inicializar el módulo de autenticación, LoginController

2.2.2 Prueba de certificación, dependencia de importación, clase de configuración de gestión de seguridad

2.2.3 Prueba de autorización, permisos de configuración

2.2.4 Principio y flujo de trabajo de Spring Security

2.3 Protocolo de autenticación OAuth2

2.3.1 Proceso de inicio de sesión de autenticación del código de escaneo de WeChat

2.3.2 Aplicación de OAuth2 en este proyecto

2.3.3 Cuatro modos de autorización de OAuth2

2.3.3.1 Modo de código de autorización

2.3.3.2 Prueba del modo de código de autorización

2.3.3.3 Modo contraseña

2.3.3.4 Método de aplicación de este proyecto: código de autorización y contraseña

2.4 JWT

2.4.1 El problema del bajo rendimiento de los tokens ordinarios

2.4.2 Introducción a JWT, autenticación sin estado, cifrado simétrico y cifrado asimétrico

2.4.3 jwt frente a sesión

2.4.4 Modificar la clase de configuración del token y probar para generar tokens JWT

2.4.5 [Módulo de contenido] Importar dependencias de autenticación y clases de configuración

2.4.6 prueba httpclient, llevar token para acceder al servicio de recursos 

2.4.7 Prueba de código, la clase SecurityContextHolder obtiene la identidad del usuario

3 Autenticación de puerta de enlace

3.1 Proceso de certificación 

3.2 [Módulo de puerta de enlace] La puerta de enlace verifica uniformemente jwt y mantiene la lista blanca


1 [ Módulo de autenticación] Análisis de requisitos

1.1 ¿ Qué es la autenticación y la autorización?

Hasta ahora, el proyecto ha completado la función de publicación del curso. Una vez publicado el curso, los usuarios pueden aprender solicitando videos en la página de aprendizaje en línea. ¿Cómo registrar el proceso de aprendizaje de los estudiantes? Si desea conocer la situación de aprendizaje del estudiante, necesita conocer la información de identidad del usuario, registrar qué usuario aprende qué curso y a qué hora. Si el usuario quiere comprar un curso, también necesita conocer la información de identidad del usuario. Por tanto, lo más básico para gestionar el proceso de aprendizaje de los estudiantes es realizar la autenticación de la identidad del usuario.

El módulo de autenticación y autorización realiza las funciones de autenticación de identidad y autorización de usuario de todos los usuarios de la plataforma.

¿Qué es la autenticación de usuario?

        La autenticación de identidad del usuario significa que cuando un usuario accede a los recursos del sistema, el sistema requiere la verificación de la información de identidad del usuario y el usuario puede continuar accediendo si la identidad es legal. Las formas comunes de autenticación de identidad de usuario incluyen: inicio de sesión con nombre de usuario y contraseña, código de escaneo de WeChat, etc.

El proyecto incluye tres tipos de usuarios: estudiantes, profesores de instituciones de aprendizaje y operadores de plataforma. No importa qué tipo de usuario acceda a los recursos protegidos del proyecto, se requiere autenticación de identidad. Por ejemplo: para publicar una operación de curso, el profesor de la institución de aprendizaje primero debe iniciar sesión exitosamente en el sistema y luego ejecutar la operación de publicación del curso. Para crear un pedido, el usuario estudiante primero debe iniciar sesión en el sistema antes de crear un pedido. Como se muestra abajo:

¿Qué es la autorización de usuario?

        Después de que el usuario pasa la autenticación para acceder a los recursos del sistema, el sistema juzgará si el usuario tiene permiso para acceder a los recursos y solo permitirá el acceso a los recursos del sistema con permiso, y los recursos sin permiso no serán accesibles. llamada autorización de usuario. Por ejemplo, cuando un usuario publica un curso, el sistema primero lo autentica. Después de pasar la autenticación, continúa juzgando si el usuario tiene permiso para publicar cursos. Si el usuario no tiene permiso, se niega a continuar accediendo. el sistema. Como se muestra abajo:

1.2 Proceso de Negocio

1.2.1 Autenticación unificada

El proyecto incluye tres tipos de usuarios: estudiantes, profesores de instituciones de aprendizaje y operadores de plataformas. Los tres tipos de usuarios utilizarán una entrada de autenticación unificada , como se muestra en la siguiente figura:

El usuario ingresa el número de cuenta y la contraseña para enviar la autenticación y la operación continúa si se aprueba la autenticación.

El servicio de autenticación unificado del proyecto acepta la solicitud de autenticación del usuario, como se muestra en la siguiente figura:

A través de la autenticación, el servicio de autenticación emite un token al usuario , que equivale a un pase para acceder al sistema, y ​​el usuario toma el token para acceder a los recursos del sistema.

1.2.2 Inicio de sesión único SSO

Este proyecto se basa en una arquitectura de microservicios. Los microservicios incluyen: servicios de gestión de contenidos, servicios de gestión de activos multimedia, servicios de centros de aprendizaje, servicios de gestión de sistemas, etc. Para mejorar la experiencia del usuario, los usuarios solo necesitan autenticarse una vez. para acceder a múltiples accesos al sistema, esta función se denomina inicio de sesión único.

El inicio de sesión único (SSO), denominado SSO, es actualmente una de las soluciones de integración empresarial empresarial más populares. La definición de SSO es que en múltiples sistemas de aplicaciones, los usuarios solo necesitan iniciar sesión una vez para acceder a todos los sistemas de aplicaciones de confianza mutua.

Como se muestra en la siguiente figura, los usuarios solo necesitan autenticarse una vez para acceder a múltiples sistemas con derechos de acceso:

1.2.3 Certificación de terceros

Para mejorar la experiencia del usuario, muchos sitios web tienen la función de inicio de sesión con código de escaneo, como: inicio de sesión con código de escaneo WeChat, inicio de sesión con código de escaneo QQ, etc. La ventaja de escanear el código para iniciar sesión es que el usuario no necesita ingresar el número de cuenta y la contraseña, y la operación es simple. Otra ventaja es que favorece el intercambio de información del usuario. La ventaja de Internet es Compartir recursos. Los usuarios también son un tipo de recurso. Es muy difícil. Si se proporciona el inicio de sesión con el código de escaneo de WeChat, se ahorrará el costo del registro del usuario, lo cual es un medio de promoción muy efectivo.

El principio de inicio de sesión con código de escaneo de WeChat es el uso de autenticación de terceros, como se muestra en la siguiente figura:

2 Marco de autorización y autenticación de Spring Security

2.1 Introducción a la seguridad de Spring

La función de autenticación es una función que casi todo proyecto debe tener y no tiene nada que ver con el negocio, existen muchos frameworks de autenticación en el mercado, como: Apache Shiro, CAS, Spring Security, etc. Dado que este proyecto se basa en la tecnología Spring Cloud, Spring Security es parte de la familia Spring y está bien integrado con Spring Cloud, por lo que este proyecto elige Spring Security como marco técnico del servicio de autenticación.

Spring Security es un marco de control de acceso y autenticación potente y altamente personalizable , que es un marco que se centra en proporcionar autenticación y autorización para aplicaciones Java.

Página de inicio del proyecto Spring Security: https://spring.io/projects/spring-security

Seguridad de Spring Cloud: https://spring.io/projects/spring-cloud-security

2.2 Primeros pasos con la autenticación y autorización

2.2.1 Inicializar el módulo de autenticación, LoginController

A continuación, utilizamos el marco Spring Security para construir rápidamente un sistema de funciones de autenticación y autorización.

1. Implementar el proyecto del servicio de autenticación.

Crear proyecto xuecheng-plus-auth

Este proyecto es un proyecto de arranque de primavera ordinario que puede conectarse a la base de datos.

Este proyecto no tiene la función de autenticación y autorización .

2. Crea una base de datos

Crear base de datos xc_users

Importe el script xcplus_users.sql en los materiales del curso.

configuración 

Agregue auth-service-dev.yaml en nacos:

server:
  servlet:
    context-path: /auth
  port: 63070
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.101.65:3306/xc1010_users?serverTimezone=UTC&userUnicode=true&useSSL=false&
    username: root
    password: mysql

arranque.yml:

spring:
  application:
    name: auth-service
  cloud:
    nacos:
      server-addr: 192.168.101.65:8848
      discovery:
        namespace: dev402
        group: xuecheng-plus-project
      config:
        namespace: dev402
        group: xuecheng-plus-project
        file-extension: yaml
        refresh-enabled: true
        shared-configs:
          - data-id: swagger-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true
          - data-id: logging-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true
          - data-id: feign-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true

  profiles:
    active: dev

El contenido inicial del controlador conectado

El proyecto inicial viene con una clase de Controlador, de la siguiente manera:

package com.xuecheng.auth.controller;


/**
 * @author Mr.M
 * @version 1.0
 * @description 测试controller
 * @date 2022/9/27 17:25
 */
@Slf4j
@RestController
public class LoginController {

  @Autowired
  XcUserMapper userMapper;

  @RequestMapping("/login-success")
  public String loginSuccess(){

      return "登录成功";
  }


  @RequestMapping("/user/{id}")
  public XcUser getuser(@PathVariable("id") String id){
    XcUser xcUser = userMapper.selectById(id);
    return xcUser;
  }
//先不添加授权,用于测试
  @RequestMapping("/r/r1")
  public String r1(){
    return "访问r1资源";
  }
//    @RequestMapping("/r/r1")
//    @PreAuthorize("hasAuthority('p1')")//拥有p1权限方可访问
//    public String r1() {
//        return "访问r1资源";
//    }
  @RequestMapping("/r/r2")
  public String r2(){
    return "访问r2资源";
  }



}

Inicie el proyecto e intente acceder a http://localhost:63070/auth/r/r1:

Acceder a la información del usuario: http://localhost:63070/auth/user/52

Todas las pruebas anteriores son normales, lo que indica que la implementación del proyecto fue exitosa.

Debido a que SpringSecurity no se ha importado, puede acceder directamente a la página sin estar autenticado

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

2.2.2 Prueba de certificación, dependencia de la guía, clase de configuración de gestión de seguridad

1. Importar dependencias 

A continuación, integre la seguridad de Spring con el proyecto de autenticación de autenticación y agregue las dependencias requeridas por Spring Security a pom.xml.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

Reinicie el proyecto, visite http://localhost:63070/auth/r/r1

Ingrese automáticamente a la página de inicio de sesión /login. /login es proporcionado por Spring Security . No necesita desarrollarlo. Hay varios estilos CSS en esta página que se cargarán un poco más lento, como se muestra en la siguiente figura:

tabla de usuarios:

¿Cuál es el número de cuenta y la contraseña? El siguiente paso requiere la configuración de seguridad.

2. Cree una clase de configuración de gestión de seguridad.

WebSecurityConfig.java para configurar:

Se requieren tres partes:

1. Información del usuario

Configurar dos usuarios en memoria: zhangsan, lisi

El usuario de zhangsan tiene la autoridad de p1.

El usuario de Lisi tiene la autoridad de p2.

2. Método de contraseña

Temporal en texto plano

3. Mecanismo de interceptación de seguridad

Las solicitudes que comienzan con /r/** requieren autenticación

Inicie sesión exitosamente en la página de éxito

El código se muestra a continuación:

 Utilice temporalmente el método de texto sin formato, solo para pruebas

/**
 * @author Mr.M
 * @version 1.0
 * @description 安全管理配置
 * @date 2022/9/26 20:53
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//配置用户信息服务
@Bean
public UserDetailsService userDetailsService() {
    //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
            manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
    manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
    return manager;
}

    @Bean
    public PasswordEncoder passwordEncoder() {
        //密码为明文方式
        return NoOpPasswordEncoder.getInstance();
    }

    //配置安全拦截机制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/r/**").authenticated()//访问/r开始的请求需要认证通过
                .anyRequest().permitAll()//其它请求全部放行
                .and()
                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success
                http.logout().logoutUrl("/logout");//退出地址
    }
}

3. Reinicie el proyecto y pruebe.

0. Inicie sesión: visite http://localhost:63070/login para iniciar sesión en "zhangsan" y "123"

1. Acceder al usuario: visite http://localhost:63070/auth/user/52 para acceder normalmente al usuario cuya identificación es 52.

2. Prueba de autenticación: visite http://localhost:63070/auth/r/r1 para mostrar la página de inicio de sesión.

La cuenta es zhangsan y la contraseña es 123. Si la contraseña ingresada es incorrecta, la autenticación fallará. Si la contraseña se ingresa correctamente, el inicio de sesión será exitoso.

¿Por qué se puede acceder normalmente a /auth/user/52, pero la página de inicio de sesión se muestra al visitar /auth/r/r1?

Debido a que la página de cierre de sesión "http.logout().logoutUrl("/logout");" está configurada en el controlador de inicio de sesión, puede cerrar sesión visitando /logout después de una autenticación exitosa.

2.2.3 Prueba de autorización, permisos de configuración

La autenticación del usuario es controlada por Spring Security al acceder a los recursos del sistema, para determinar si el usuario tiene acceso al recurso; en caso afirmativo, continúe accediendo; en caso contrario, niegue el acceso.

La siguiente función de autorización de prueba:

1. Clase de configuración de gestión de seguridad, que configura todos los permisos de usuario.

En la clase WebSecurityConfig, configure zhangsan para que tenga permiso p1 y lisi para que tenga permiso p2.

    @Bean
    public UserDetailsService userDetailsService() {
        //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
        manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
        return manager;
    }

2. LoginController especifica la relación entre recursos y permisos.

¿Qué son los recursos del sistema?

Por ejemplo: para consultar la información de un usuario, la información del usuario es el recurso del sistema. Para acceder al recurso, debe pasar la URL, por lo que cada interfaz http que definimos en el controlador es la interfaz para acceder al recurso.

A continuación, configurar /r/r1 en el controlador requiere permisos p1 y /r/r2 requiere permisos p2.

hasAuthority('p1') indica que solo aquellos con autoridad p1 pueden acceder a él.

El código se muestra a continuación:

@RestController
public class LoginController {
    ....
    @RequestMapping("/r/r1")
    @PreAuthorize("hasAuthority('p1')")//拥有p1权限方可访问
    public String r1(){
      return "访问r1资源";
    }
   
    @RequestMapping("/r/r2")
    @PreAuthorize("hasAuthority('p2')")//拥有p2权限方可访问
    public String r2(){
      return "访问r2资源";
    }
    ...

3. Ahora reinicie el proyecto.

Al acceder a una URL que comienza con /r/, juzgará si el usuario está autenticado. De lo contrario, saltará a la página de inicio de sesión. Si ha sido autenticado, juzgará si el usuario tiene derecho de acceso a la URL. Si tiene derecho de acceso a la URL, continúe; de ​​lo contrario, se denegará el acceso.

Por ejemplo:

Visite /r/r1, salte automáticamente a la página de inicio de sesión y use zhangsan para iniciar sesión y acceder normalmente, debido a que el permiso p1 se especifica en el método de /r/r1, el usuario zhangsan tiene permiso p1, por lo que se puede acceder normalmente .

Acceda a /r/r1, use lisi para iniciar sesión y denegar el acceso, porque el usuario lisi no tiene permiso, p1 necesita denegar el acceso

Nota: Si no se agrega @PreAuthorize al acceso, este método no tiene control de autorización.

Autorización correspondiente de Zhang San Li Si: 

El proceso de organización de la autorización se muestra en la siguiente figura:

2.2.4 Principio y flujo de trabajo de Spring Security

El problema que resuelve Spring Security es el control de acceso de seguridad , y la función de control de acceso de seguridad es en realidad interceptar todas las solicitudes que ingresan al sistema y verificar si cada solicitud puede acceder a los recursos que espera . De acuerdo con el aprendizaje del conocimiento previo, se puede implementar mediante tecnologías como Filter o AOP. La protección de los recursos web de Spring Security se realiza mediante Filter , así que comience con este Filtro y profundice gradualmente el principio de Spring Security.

        Cuando se inicializa Spring Security, se creará un filtro de Servlet llamado SpringSecurityFilterChain, de tipo org.springframework.security.web.FilterChainProxy, que implementa javax.servlet.Filter, por lo que las solicitudes externas pasarán a través de esta clase, como se muestra a continuación Filtro de Spring Security diagrama de estructura de cadena:

FilterChainProxy es un proxy. Lo que realmente funciona son los filtros contenidos en SecurityFilterChain en FilterChainProxy. Al mismo tiempo, estos filtros son administrados por Spring como beans. Son el núcleo de Spring Security y tienen sus propias responsabilidades, pero no manejan directamente autenticación de usuario.No maneja directamente la autorización del usuario , pero la entrega a AuthenticationManager y AccessDecisionManager para su procesamiento.

La realización de la función de seguridad del resorte se completa principalmente con una serie de cadenas de filtros.

A continuación se describen los filtros principales y sus funciones en la cadena de filtros:

SecurityContextPersistenceFilter es la entrada y salida de todo el proceso de interceptación (es decir, el primer y último interceptor), obtendrá el SecurityContext del SecurityContextRepository configurado al comienzo de la solicitud y luego lo establecerá en SecurityContextHolder. Una vez completada la solicitud, guarde el SecurityContext retenido por SecurityContextHolder en el SecurityContextRepository configurado y borre el SecurityContext retenido por securityContextHolder;

UsernamePasswordAuthenticationFilter se utiliza para manejar la autenticación de los envíos de formularios. El formulario debe proporcionar el nombre de usuario y la contraseña correspondientes, y también hay AuthenticationSuccessHandler y AuthenticationFailureHandler para procesar después de un inicio de sesión exitoso o fallido, que se pueden cambiar según los requisitos;

FilterSecurityInterceptor se usa para proteger los recursos web, usando AccessDecisionManager para autorizar el acceso al usuario actual, que se presentó en detalle anteriormente;

ExceptionTranslationFilter puede detectar todas las excepciones de FilterChain y manejarlas. Pero solo manejará dos tipos de excepciones: AuthenticationException y AccessDeniedException, y continuará arrojando otras excepciones.

El flujo de ejecución de Spring Security es el siguiente:

  1. El nombre de usuario y la contraseña enviados por el usuario se obtienen mediante el filtro de nombre de usuario y contraseña UsernamePasswordAuthenticationFilter en SecurityFilterChain y se encapsulan como solicitud de autenticación , que suele ser la clase de implementación de UsernamePasswordAuthenticationToken.
  2. Luego, el filtro envía la autenticación al Administrador de autenticación (AuthenticationManager) para su autenticación.
  3. Una vez que la autenticación es exitosa, el administrador de identidad AuthenticationManager devuelve una instancia de autenticación llena de información (incluida la información de permiso, la información de identidad y los detalles mencionados anteriormente, pero la contraseña generalmente se elimina) a través de la consulta del Proveedor DaoAuthentication.
  4. El contenedor de contexto de seguridad SecurityContextHolder completará la autenticación con la información del paso 3 y guardará la autenticación en el contexto de seguridad mediante el método SecurityContextHolder.getContext().setAuthentication(… ) .
  5. Se puede ver que la interfaz AuthenticationManager (administrador de autenticación) es la interfaz central relacionada con la autenticación y el punto de partida para iniciar la autenticación, su clase de implementación es ProviderManager. Spring Security admite múltiples métodos de autenticación, por lo que ProviderManager mantiene una lista Lista <AuthenticationProvider> , almacena múltiples métodos de autenticación, y el trabajo de autenticación real final lo realiza AuthenticationProvider. Sabemos que la clase de implementación AuthenticationProvider correspondiente del formulario web es DaoAuthenticationProvider, y mantiene un UserDetailsService dentro que es responsable de obtener UserDetails. Finalmente, AuthenticationProvider completa UserDetails en Authentication.

2.3 Protocolo de autenticación OAuth2

2.3.1 Proceso de inicio de sesión de autenticación del código de escaneo de WeChat

La autenticación del código de escaneo de WeChat es un método de autenticación de terceros basado en el protocolo OAuth2.

El protocolo OAUTH proporciona un estándar seguro, abierto y sencillo para la autorización de los recursos de los usuarios . Al mismo tiempo, cualquier tercero puede utilizar el servicio de autenticación OAUTH y cualquier proveedor de servicios puede implementar su propio servicio de autenticación OAUTH, por lo que OAUTH está abierto.

La industria proporciona una variedad de implementaciones de OAUTH, como PHP, JavaScript, Java, Ruby y otros kits de desarrollo de lenguajes, lo que ahorra mucho tiempo a los programadores, por lo que OAuth es simple. Muchos servicios de Internet, como Open API, y muchas grandes empresas, como Google, Yahoo y Microsoft, proporcionan servicios de autenticación OAUTH, que son suficientes para demostrar que el estándar OAUTH se ha convertido gradualmente en el estándar para la autorización de recursos abiertos.

El protocolo Oauth se ha desarrollado hasta la versión 2.0, la versión 1.0 es demasiado complicada y la versión 2.0 se ha utilizado ampliamente.

Referencia: Enciclopedia oAuth_Baidu

Oauth协议: RFC 6749: El marco de autorización de OAuth 2.0

El proceso de inicio de sesión del código de escaneo de autenticación de WeChat: 

Analicemos un ejemplo de autenticación Oauth2. El sitio web del programador Dark Horse utiliza la autenticación WeChat para escanear el código para iniciar sesión:

1. El usuario hace clic en WeChat para escanear el código.

El usuario ingresa a la página de inicio de sesión del programa Dark Horse y hace clic en el ícono de WeChat para abrir la interfaz de escaneo de código de WeChat.

Aparece un código QR.

El propósito del código de escaneo de WeChat es iniciar sesión en el sitio web oficial de Dark Horse Programmer a través de la autenticación WeChat. El sitio web de Dark Horse Programmer necesita obtener la información de identidad del usuario actual de WeChat para permitir que el usuario actual inicie sesión con éxito en Sitio web de Dark Horse.

Ahora descubra algunos conceptos:

Recursos: información del usuario, almacenada en WeChat.

Propietario del recurso: el usuario es el propietario de los recursos de información del usuario.

Servicio de autenticación: WeChat es responsable de autenticar la identidad del usuario actual y emitir tokens para el cliente.

Cliente: el cliente llevará el token para solicitar WeChat para obtener información del usuario. El sitio web del programador de Dark Horse es el cliente y el sitio web de Dark Horse debe abrirse en un navegador.

2. El usuario autoriza al sitio web de dark horse a acceder a la información del usuario

El propietario del recurso escanea el código QR para indicar que el propietario del recurso solicita la autenticación de WeChat, y la autenticación de WeChat devuelve la página de autorización al teléfono móvil del usuario, como se muestra en la siguiente figura:

Pregunte al usuario si desea autorizar al programador de Dark Horse a acceder a su información de usuario en WeChat, el usuario hace clic en "Confirmar inicio de sesión" para aceptar la autorización y el servidor de autenticación de WeChat emitirá un código de autorización al sitio web del programador de Dark Horse.

Solo cuando el propietario del recurso acepta WeChat se le puede permitir al sitio web de Dark Horse acceder al recurso.

3. El sitio web del programador de Dark Horse obtiene el código de autorización.

4. Traiga el código de autorización para solicitar al servidor de autenticación WeChat que solicite un token.

Esta interacción es invisible para el usuario.

5. El servidor de autenticación WeChat responde con un token al sitio web del programador Dark Horse.

Esta interacción es invisible para el usuario.

6. El sitio web del programador Dark Horse lleva un token para solicitar al servidor de recursos WeChat que obtenga recursos, es decir, información del usuario.

7. El servidor de recursos devuelve el recurso protegido, es decir, la información del usuario.

8. El sitio web de Dark Horse recibe información del usuario y el usuario inicia sesión correctamente en el sitio web de Dark Horse en este momento.

Después de comprender el proceso de escanear el código WeChat para iniciar sesión en el sitio web de Dark Horse, el siguiente paso es comprender el proceso de autenticación de Oauth2.0, de la siguiente manera:

Citado del protocolo Oauth2.0 rfc6749 RFC 6749: El marco de autorización de OAuth 2.0

Oauth2 incluye las siguientes funciones:

1. Cliente

No almacena recursos en sí y necesita solicitar los recursos del servidor de recursos mediante la autorización del propietario del recurso, como: cliente móvil, navegador, etc.

El sitio web de Dark Horse en el ejemplo anterior es el cliente y debe abrirse a través de un navegador.

2. Propietario del recurso

Recurso: Información del usuario

Generalmente un usuario, pero también una aplicación, es decir, el propietario del recurso.

A indica que el cliente solicita autorización al propietario del recurso.

B significa que el propietario del recurso autoriza al cliente, el sitio web de Dark Horse, a acceder a su propia información de usuario.

3. Servidor de autorización (también llamado servidor de autenticación)

Por ejemplo, el servidor WeChat. El servidor de autenticación autentica al propietario del recurso y también autentica al cliente y emite tokens.

El cliente C, es decir, el sitio web de Dark Horse, lleva el código de autorización para solicitar autenticación.

D se autentica emitiendo un token.

4. Servidor de recursos

Como base de datos. Un servidor que almacena recursos.

E significa que el cliente, es decir, el sitio web de Dark Horse, lleva el token y solicita al servidor de recursos que obtenga recursos.

F indica que el servidor de recursos proporciona recursos protegidos después de que pasa el token de verificación.

2.3.2 Aplicación de OAuth2 en este proyecto

Oauth2 es un protocolo de autorización abierto estándar . Las aplicaciones pueden usar Oauth2 según sus propios requisitos. Este proyecto utiliza Oauth2 para lograr los siguientes objetivos:

1. Xuecheng accede a recursos de sistemas de terceros (como WeChat) en línea.

Este proyecto necesita acceder al inicio de sesión del código de escaneo de WeChat, por lo que este proyecto necesita utilizar el protocolo OAuth2 para acceder a la información del usuario en WeChat .

2. El sistema externo accede a los recursos de Xuecheng Online.

De manera similar, cuando un sistema de terceros desea acceder a los recursos del sitio web de Xuecheng Online, también puede basarse en el protocolo OAuth2.

3. Front-end de Xuecheng Online (cliente) Acceda a los recursos de los microservicios de Xuecheng Online.

Este proyecto es una arquitectura de separación de front-end y back-end, y el acceso de front-end a los recursos de microservicios también se puede autenticar según el protocolo OAuth2.

2.3.3 Cuatro modos de autorización de OAuth2

Spring Security admite la autenticación OAuth2. OAuth2 proporciona cuatro modos de autorización : modo de código de autorización, modo de contraseña, modo simplificado y modo de cliente. El ejemplo de inicio de sesión con código de escaneo de WeChat mencionado anteriormente se basa en el modo de código de autorización. Entre estos cuatro modos, la autorización El modo de código y el modo de contraseña se utilizan ampliamente.

Esta sección utiliza Spring Security para demostrar el modo de código de autorización y el modo de contraseña. Consulte la información relevante para los otros dos.

2.3.3.1 Modo de código de autorización

El ejemplo de inicio de sesión con código de escaneo de WeChat mencionado anteriormente se basa en el modo de código de autorización. 

Varios modos de autorización de OAuth2 son obtener tokens de diferentes maneras según diferentes escenarios de aplicación . El objetivo final es obtener tokens emitidos por servicios de autenticación y finalmente obtener recursos a través de tokens .

La comprensión simple del modo de código de autorización es utilizar el código de autorización para obtener el token . Si desea obtener el token, primero debe obtener el código de autorización. La adquisición del código de autorización requiere la autorización y el consentimiento del propietario del recurso. para obtenerlo.

La siguiente figura es un diagrama interactivo del modo del código de autorización:

También tome el inicio de sesión del código de escaneo WeChat del sitio web de Dark Horse como ejemplo para ilustrar:

1. El usuario abre el navegador.

2. Acceder al sitio web del cliente, concretamente Dark Horse, a través de un navegador.

3. El usuario solicita autorización al servicio de autenticación a través del navegador, y al solicitar autorización se llevará la URL del cliente, que es la dirección de redirección para emitir el código de autorización.

4. El servicio de autenticación devuelve una página de autorización al propietario del recurso.

5. El titular del recurso lo autoriza y acepta personalmente.

6. Envíe el acuerdo de autorización al servicio de autenticación a través del navegador.

7. El servicio de autenticación redirige a la dirección del cliente y lleva el código de autorización.

8. El cliente, el sitio web de Dark Horse, recibe el código de autorización.

9. El cliente lleva el código de autorización para solicitar un token del servicio de autenticación.

10. El servicio de autenticación emite un token al cliente.

2.3.3.2  Prueba del modo de código de autorización

Para probar el modo de autorización, primero debe configurar el servidor de autorización, es decir, el servidor de autenticación en la figura anterior, y debe configurar el servicio de autorización y la política de token.

1. Copie la clase de configuración del servicio de autorización AuthorizationServer.java y la clase de configuración de la política de token TokenConfig.java de los materiales del curso al paquete de configuración del servicio de autenticación.

Descripción : AuthorizationServer está marcado con la anotación @EnableAuthorizationServer y hereda AuthorizationServerConfigurerAdapter para configurar el servidor de autorización OAuth2.0.

package com.xuecheng.auth.config;
*/
 @Configuration
 @EnableAuthorizationServer
 public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
 ...

La clase principal AuthorizationServerConfigurerAdapter requiere la configuración de las siguientes clases:

public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
    public AuthorizationServerConfigurerAdapter() {}
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {}
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {}
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {}
}

1 ) ClientDetailsServiceConfigurer : se utiliza para configurar el servicio de detalles del cliente (ClientDetailsService),

¿Puede cualquier cliente acceder a su servicio de autenticación a voluntad? La respuesta es no, el proveedor de servicios le dará al cliente aprobado para el acceso una identidad, que se utiliza como credencial para el acceso, incluida la ID del cliente y la clave secreta del cliente, y configurará la información detallada del cliente aprobado para el acceso. accede aquí.

2 ) AuthorizationServerEndpointsConfigurer : Se utiliza para configurar los puntos finales de acceso y los servicios de token del token (token).

3 ) AuthorizationServerSecurityConfigurer : se utiliza para configurar las restricciones de seguridad del punto final del token.

2. TokenConfig es una clase de configuración de política de token

Utilice temporalmente InMemoryTokenStore para almacenar tokens en la memoria primero y configure información como el período de validez de los tokens de la siguiente manera:

    //令牌管理服务
    @Bean(name="authorizationServerTokenServicesCustom")
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service=new DefaultTokenServices();
        service.setSupportRefreshToken(true);//支持刷新令牌
        service.setTokenStore(tokenStore);//令牌存储策略
        service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
        return service;
    }

3. Configurar el bean de gestión de autenticación.

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    ....

Reinicie el servicio de autenticación.

1. obtener solicitud para obtener el código de autorización

Información: http://localhost:63070/auth/oauth/authorize?client_id=XcWebApp&response_type=code&scope=all&redirect_uri=http://www.51xuecheng.cn

La lista de parámetros es la siguiente:

  • client_id: ID de acceso del cliente.
  • tipo_respuesta: el modo del código de autorización se fija como código.
  • alcance: autoridad del cliente.
  • redirigir_uri: redirigir uri, cuando la aplicación del código de autorización sea exitosa, saltará a esta dirección y el parámetro de código (código de autorización) se adjuntará detrás de ella.

Ingrese la cuenta zhangsan, contraseña 123 para iniciar sesión correctamente, ingrese /oauth/authorize?client_id=XcWebApp&response_type=code&scope=all&redirect_uri=http://www.51xuecheng.cn

Mostrar página de autorización

¿Autorizar a "XcWebApp" a acceder a sus propios recursos protegidos?

Elija Aceptar.

2. Si la solicitud tiene éxito , redirija a http://www.51xuecheng.cn/?code=código de autorización, por ejemplo: http://www.51xuecheng.cn/?code=Wqjb5H

3. Utilice la herramienta httpclient para enviar una solicitud de publicación para solicitar un token.

### 授权码模式
### 第一步申请授权码(浏览器请求)/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.51xuecheng.cn
### 第二步申请令牌
POST {
   
   {auth_host}}/auth/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=authorization_code&code=CTvCrB&redirect_uri=http://www.51xuecheng.cn

La lista de parámetros es la siguiente.

  • client_id: ID de acceso del cliente.
  • client_secret: clave secreta del cliente.
  • Grant_type: tipo de autorización, complete el código de autorización, que indica el modo del código de autorización
  • código: Código de autorización, que es el código de autorización que acaba de obtener. Nota: el código de autorización no será válido después de usarse solo una vez y deberá solicitarlo nuevamente.
  • redirección_uri: la URL de redirección al solicitar el código de autorización debe ser coherente con la redirección_uri utilizada al solicitar el código de autorización.

La solicitud de un token con éxito se ve así:

{
  "access_改成自己的token": "368b1ee7-a9ee-4e9a-aae6-0fcab243aad2",
  "token_type": "bearer",
  "refresh_token": "3d56e139-0ee6-4ace-8cbe-1311dfaa991f",
  "expires_in": 7199,
  "scope": "all"
}

ilustrar:

1. access_token, token de acceso , utilizado para acceder a los recursos.

2. token_type, el portador es un tipo de token definido en RFC6750. Cuando se lleva un token para acceder a recursos, es necesario agregar contenido del token de espacio de portador en el encabezado.

3. refresco_token, cuando el token está a punto de caducar, el token de actualización se puede utilizar para generar el token nuevamente.

4. expires_in: tiempo de vencimiento (segundos)

5. alcance, el alcance de autoridad del token, el servidor puede autorizar el token de acuerdo con el alcance de autoridad del token.

2.3.3.3 Modo contraseña

El modo de contraseña es más simple que el modo de código de autorización: el modo de código de autorización requiere un navegador para que el usuario se autorice, y el modo de contraseña no necesita un navegador, como se muestra en la siguiente figura:

1. El propietario del recurso proporciona la cuenta y la contraseña.

2. El cliente solicita un token del servicio de autenticación y la solicitud incluye el número de cuenta y la contraseña.

3. El servicio de autenticación verifica que el número de cuenta y la contraseña sean correctos y emite el token .

empezar a probar:

1. Solicitud POST para obtener el token

/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=contraseña&username=shangsan&password=123

La lista de parámetros es la siguiente:

  • client_id: ID de acceso del cliente.
  • client_secret: clave secreta del cliente.
  • Grant_type: tipo de autorización, complete la contraseña para indicar el modo de contraseña
  • nombre de usuario: nombre de usuario del propietario del recurso.
  • contraseña: contraseña del propietario del recurso.

2. El servidor de autorización envía el token (access_token) al cliente.

Prueba con httpclient

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

Ejemplo de devolución:

{
  "access_t改成自己的oken": "368b1ee7-a9ee-4e9a-aae6-0fcab243aad2",
  "token_type": "bearer",
  "refresh_token": "3d56e139-0ee6-4ace-8cbe-1311dfaa991f",
  "expires_in": 6806,
  "scope": "all"
}

        Este modo es muy simple, pero significa que la información confidencial del usuario se filtra directamente al cliente . Entonces, esto significa que este modo solo se puede usar cuando el cliente lo desarrollamos nosotros mismos.

2.3.3.4 Método de aplicación de este proyecto: código de autorización y contraseña

Al demostrar el modo de código de autorización y el modo de contraseña, el modo de código de autorización es adecuado para situaciones en las que el cliente y el servicio de autenticación no están en el mismo sistema, por lo que este proyecto utiliza el modo de código de autorización para completar la autenticación de escaneo de código WeChat. Este proyecto utiliza el modo de contraseña como método de autenticación para que el front-end solicite microservicios.

2.4 JWT

2.4.1 El problema del bajo rendimiento de los tokens ordinarios

Los tokens ordinarios deben ser verificados por el "servicio de autenticación de llamadas remotas del servicio de recursos" y el rendimiento es bajo. 

El cliente solicita el token y luego lleva el token para acceder al recurso, y el servidor de recursos verificará la legitimidad del token.

¿Cómo verifica el servidor de recursos la legitimidad del token?

Tomemos el modo de contraseña de OAuth2 como ejemplo para ilustrar:

Instrucciones del paso 4:

1. El cliente lleva el token para acceder al servicio de recursos para obtener recursos.

2. El servicio de recursos solicita de forma remota al servicio de autenticación que verifique la legitimidad del token.

3. Si el token es válido, el servicio de recursos devuelve el recurso al cliente.

Hay un problema aquí:

Es decir, el token de verificación debe solicitar el servicio de autenticación de forma remota, cada acceso del cliente se verificará de forma remota y el rendimiento de ejecución es bajo .

El servicio de recursos verifica el token en sí. 

Si el servicio de recursos puede verificar la legitimidad del token por sí mismo, se ahorrará el costo de solicitar remotamente el servicio de autenticación y se mejorará el rendimiento. Como se muestra abajo:

Cómo resolver el problema anterior y realizar el servicio de recursos para verificar el token por sí mismo .

El problema anterior se puede resolver utilizando el formato JWT del token. Después de que el usuario pase la autenticación, se obtendrá un token JWT. El token JWT ya incluye información relacionada con el usuario. El cliente solo necesita llevar el JWT para acceder al Servicio de recursos. El servicio de recursos se basa en el .acuerdo

2.4.2 Introducción a JWT, autenticación sin estado, cifrado simétrico y cifrado asimétrico

JSON Web Token (JWT) es una tecnología de token de red que utiliza el formato JSON para transferir datos. Es un estándar industrial abierto (RFC 7519) que define un formato de protocolo conciso y autónomo para la comunicación Las dos partes pasan el objeto json, y la información transmitida puede verificarse y ser confiable después de la firma digital. Puede usar el algoritmo HMAC o usar el par de claves públicas y privadas RSA para firmar y evitar la manipulación del contenido. Sitio web oficial: Tokens web JSON - jwt.io

La autenticación sin estado se puede lograr utilizando JWT.

Autenticación con estado:

El servidor guarda la información del cliente. El servidor no necesita transportar información del usuario para cada solicitud.

El método tradicional basado en sesión es la autenticación con estado. Después de que un usuario inicia sesión exitosamente, la información de identidad del usuario se almacena en el servidor en forma de sesión , lo que aumenta la presión de almacenamiento en el servidor, y este método no es adecuado para su aplicación en sistemas distribuidos.

Como se muestra en la figura siguiente, cuando un usuario accede a un servicio de aplicación, cada servicio de aplicación irá al servidor para verificar la información de la sesión. Si el usuario no existe en la sesión, significa que el usuario no ha iniciado sesión. esta vez se realizará una nueva autenticación. La solución a este problema es copiar sesión y pegar sesión.

autenticación sin estado 

El servidor no almacena información del cliente. El servidor necesita transportar información del usuario cada vez que la solicita. 

Si la autenticación se implementa en un sistema distribuido basado en tecnología de token, el servidor no necesita almacenar la sesión , pero puede almacenar la información de identidad del usuario en el token . Una vez autenticado el usuario, el servicio de autenticación emite un token al usuario. y el usuario almacena el token en el cliente analiza la información del usuario del. Al acceder al servicio de la aplicación, lleve el token para acceder y el servidor . Este proceso se conoce como autenticación sin estado.

sesión:

Debido a que la sesión se almacena en el servidor, el intercambio de datos entre varias máquinas debe realizarse en un entorno distribuido. La sesión generalmente debe combinarse con cookies para implementar la autenticación, por lo que el navegador debe admitir cookies, por lo que el terminal móvil no puede usar el esquema de autenticación de sesión.

Ventajas de los tokens JWT:

1. jwt se basa en json, que es muy conveniente para el análisis.

2. Se puede personalizar contenido enriquecido en el token, que es fácil de expandir.

3. A través de un algoritmo de cifrado asimétrico y tecnología de firma digital, JWT evita la manipulación y tiene alta seguridad .

4. Los servicios de recursos pueden utilizar JWT para completar la autorización sin depender de los servicios de autenticación .

defecto:

El token JWT es más largo y ocupa mucho espacio de almacenamiento.

A continuación se muestra un ejemplo de un token JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJ6aGFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2NjQyNTQ2NzIsImF1dGhvcml0aWVzIjpbInAxIl0sImp0aSI6Ijg4OTEyYjJkLTVkMDUtNGMxNC1iYmMzLWZkZTk5NzdmZWJjNiIsImNsaWVudF9pZCI6ImMxIn0.wkDBL7roLrvdBG2oGnXeoXq-zZRgE9IVV2nxd-ez_oA

El token JWT consta de tres partes, cada parte está separada por un punto (.), por ejemplo: xxxxx.yyyyy.zzzzz

Encabezamiento       

  El encabezado incluye el tipo de token (es decir, JWT) y el algoritmo hash utilizado (por ejemplo, HMAC SHA256 o RSA).

  Un ejemplo es el siguiente:

  A continuación se muestra el contenido de la sección Encabezado.

   {
    "alg": "HS256",
    "typ": "JWT"
  }

  Utilice Base64Url para codificar el contenido anterior y obtenga una cadena que sea la primera parte del token JWT.

 Carga útil

  La segunda parte es la carga, y el contenido también es un objeto json, es el lugar para almacenar información válida, puede almacenar los campos de información proporcionados por jwt, como: iss (emisor), exp (marca de tiempo de vencimiento), sub (orientado al usuario), etc. También puede personalizar los campos.

  No se recomienda esta sección para almacenar información confidencial, ya que esta sección puede decodificar y restaurar el contenido original.

  Finalmente, codifique la segunda parte de la carga útil con Base64Url para obtener una cadena que sea la segunda parte del token JWT.

  un ejemplo:

  {
    "sub": "1234567890",
    "name": "456",
    "admin": true
  }

 Firma

  La tercera parte es la firma , que se utiliza para evitar que se altere el contenido del jwt .

  Esta parte usa base64url para codificar las dos primeras partes. Después de codificar, use puntos (.) para conectarse para formar una cadena y finalmente use el algoritmo de firma declarado en el encabezado para firmar.

  un ejemplo:

  HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret)

base64UrlEncode (encabezado): la primera parte del token jwt.

base64UrlEncode (carga útil): la segunda parte del token jwt.

secreto: la clave secreta utilizada para firmar.

¿Por qué JWT puede evitar la manipulación?

Mientras la clave no se filtre, jwt no será manipulado. 

La tercera parte de JWT utiliza un algoritmo de firma para firmar el contenido de la primera y segunda parte. El algoritmo de firma comúnmente utilizado es HS256, y md5, sha, etc. son comunes. El algoritmo de firma necesita usar una clave para firmar, y la clave no se divulga al público. y la firma es irreversible. Si un tercero cambia el contenido, el servidor no podrá verificar la firma. Para garantizar que la firma de verificación sea correcta, el contenido y la clave deben ser consistentes con los anteriores a la firma.

Cifrado simétrico

En la figura anterior, podemos ver que el servicio de autenticación y el servicio de recursos usan la misma clave , lo que se llama cifrado simétrico , y el cifrado simétrico es eficiente. La desventaja es que una vez que se filtra la clave, el token jwt se puede falsificar .

cifrado asimétrico 

JWT también puede utilizar cifrado asimétrico. El servicio de autenticación mantiene la clave privada y envía la clave pública a clientes confiables y servicios de recursos. La clave pública y la clave privada están emparejadas , y la clave pública y la clave privada emparejadas pueden funcionar normalmente. y descifrado, el cifrado asimétrico es ineficiente pero más seguro que el cifrado simétrico .

2.4.3  jwt frente a sesión

Proceso de autenticación
Proceso de autenticación basado en sesiones.

El usuario ingresa el nombre de usuario y la contraseña en el navegador, y el servidor genera una sesión después de pasar la verificación de la contraseña y la guarda en la base de datos.

El servidor genera un ID de sesión para el usuario y coloca una cookie con el ID de sesión en el navegador del usuario, al que se accederá con esta información de cookie en solicitudes posteriores.

El servidor obtiene la cookie y encuentra la base de datos obteniendo el ID de sesión en la cookie para determinar si la solicitud actual es válida.

Proceso de autenticación basado en JWT

El usuario ingresa el nombre de usuario y la contraseña en el navegador, y el servidor genera un token después de pasar la verificación de la contraseña y lo guarda en la base de datos.

El front-end obtiene el token, lo almacena en una cookie o almacenamiento local y accede con esta información del token en solicitudes posteriores.

El servidor obtiene el valor del token y juzga si el token actual es válido buscando en la base de datos.

Ventajas y desventajas
JWT se almacena en el cliente y no se requiere trabajo adicional en un entorno distribuido. Dado que la sesión se almacena en el lado del servidor, el intercambio de datos entre varias máquinas debe realizarse en un entorno distribuido. La sesión generalmente debe combinarse con cookies para implementar la autenticación, por lo que el navegador debe admitir cookies, por lo que el terminal móvil no puede Utilice el esquema de autenticación de sesión.

Seguridad
La carga útil del JWT está codificada en base64, por lo que no se pueden almacenar datos confidenciales en el JWT. La información de la sesión se almacena en el lado del servidor, lo cual es relativamente más seguro.

Rendimiento
Después de la codificación, el JWT será muy largo. El tamaño límite de la cookie es generalmente 4k y es posible que la cookie no quepa, por lo que el JWT generalmente se coloca en el almacenamiento local. Y cada solicitud http realizada por el usuario en el sistema llevará el JWT en el encabezado, y el encabezado de la solicitud HTTP puede ser más grande que el cuerpo. El ID de sesión es solo una cadena corta, por lo que la solicitud HTTP usando JWT es mucho más costosa que usar la sesión.

La apatridia única
es la característica de JWT, pero también conduce a este problema: JWT es única. Si desea modificar el contenido interno, debe emitir un nuevo JWT.

No se puede descartar
Una vez que se emite un JWT, seguirá siendo válido hasta que caduque y no se puede descartar a mitad de camino. Si desea descartar, un método de procesamiento común es combinar redis

Renovación
Si utiliza JWT para la gestión de sesiones, el esquema de renovación de cookies tradicional generalmente está integrado en el marco. La sesión es válida por 30 minutos. Si hay un acceso dentro de los 30 minutos, el período de validez se actualizará a 30 minutos. De la misma forma, para cambiar la hora válida del JWT, se debe emitir un nuevo JWT.

La forma más sencilla es actualizar el JWT cada vez que se realiza una solicitud, es decir, cada solicitud HTTP devuelve un nuevo JWT. Este método no solo es violento y poco elegante, sino que también requiere cifrado y descifrado JWT para cada solicitud, lo que provocará problemas de rendimiento. Otro método es establecer el tiempo de vencimiento para cada JWT por separado en Redis y actualizar el tiempo de vencimiento de JWT cada vez que visite

Elegir JWT o
JWT de sesión tiene muchas desventajas, pero en un entorno distribuido, no es necesario implementar un intercambio de datos adicional entre varias máquinas como la sesión, aunque el intercambio de datos entre varias máquinas de seesion se puede realizar mediante sesión fija, intercambio de sesión y replicación de sesión. , sesión persistente, terracoa Implemente varias soluciones maduras, como la replicación de seesion, para resolver este problema. Pero JWT no requiere trabajo adicional, ¿no es bueno usar JWT? Y la única deficiencia de JWT se puede compensar combinando redis
 

2.4.4 Modificar la clase de configuración del token y probar para generar tokens JWT

En la clase de configuración de token en el servicio de autenticación , configure el servicio de token jwt para generar un token en formato jwt.

package com.xuecheng.auth.config;

@Configuration
public class TokenConfig {

//定义密钥
    private String SIGNING_KEY = "mq123";

    @Autowired
    TokenStore tokenStore;

//    @Bean
//    public TokenStore tokenStore() {
//        //使用内存存储令牌(普通令牌)
//        return new InMemoryTokenStore();
//    }

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }

    //令牌管理服务
    @Bean(name="authorizationServerTokenServicesCustom")
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service=new DefaultTokenServices();
        service.setSupportRefreshToken(true);//支持刷新令牌
        service.setTokenStore(tokenStore);//令牌存储策略

        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
        service.setTokenEnhancer(tokenEnhancerChain);

        service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
        return service;
    }
}

Reinicie el servicio de autenticación.

Utilice httpclient para solicitar un token a través del modo contraseña

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

Un ejemplo de generación de jwt es el siguiente:

{
  "access改成自己的_token": "eyJhbXxx.-9SKI-qUqKhKcs8Gb80Rascx-JxqsNZxxXoPo82d8SM",
  "token_type": "bearer",
  "refresh_token": "eyJhbXxx.eyJhdXxx.Wsw1JXxxx",
  "expires_in": 7199,
  "scope": "all",
  "jti": "e9d3d0fd-24cb-44c8-8c10-256b34f8dfcc"
}

1. access_token, el token jwt generado, utilizado para acceder a los recursos.

2. token_type, el portador es un tipo de token definido en RFC6750. Cuando se lleva jwt para acceder a recursos, es necesario agregar el contenido del token jwt del portador en el encabezado.

3. refresco_token, cuando el token jwt esté a punto de caducar, utilice el token de actualización para generar el token jwt nuevamente.

4. expires_in: tiempo de vencimiento (segundos)

5. alcance, el alcance de autoridad del token, el servidor puede autorizar el token de acuerdo con el alcance de autoridad del token.

6. jti: el identificador único del token.

Verificar token jwt

Podemos verificar el token jwt a través de la interfaz check_token que viene con Spring Security, simplemente agregue "/check_token" a la ruta 

###校验jwt令牌
POST {
   
   {auth_host}}/oauth/check_token?token=eyJhbGciOXxx.qy46CXxx

La información del usuario analizada es la siguiente:


{
  "aud": [
    "res1"
  ],
  "user_name": "zhangsan",
  "scope": [
    "all"
  ],
  "active": true,
  "exp": 1664371780,
  "authorities": [
    "p1"
  ],
  "jti": "f0a3cdeb-399d-48f0-8804-eca638ad8857",
  "client_id": "c1"
}

2.4.5 [Módulo de contenido] Importar dependencias de autenticación y clases de configuración

Después de obtener el token jwt, el siguiente paso es llevar el token para acceder a los recursos en el servicio de recursos. Cada microservicio de este proyecto es el servicio de recursos, como: servicio de administración de contenido, el cliente solicita el token jwt y lo lleva. el jwt al servicio de gestión de contenidos para consultar la información del curso. En este momento, el servicio de gestión de contenidos necesita verificar el jwt. Sólo cuando el jwt sea válido se podrá continuar con el acceso. Como se muestra abajo:

1. Agregue dependencias al proyecto content-api del servicio de gestión de contenidos.

<!--认证相关-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

2. Crea una clase de configuración.

Cree una clase de configuración de servicio de recursos ResouceServerConfig y una clase de configuración de token TokenConfig en el paquete de configuración del proyecto de API de administración de contenido .

/**
 * @description 资源服务配置
 * @author Mr.M
 * @date 2022/10/18 16:33
 * @version 1.0
 */
 @Configuration
 @EnableResourceServer
 @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
 public class ResouceServerConfig extends ResourceServerConfigurerAdapter {


  //资源服务标识
  public static final String RESOURCE_ID = "xuecheng-plus";

  @Autowired
  TokenStore tokenStore;

  @Override
  public void configure(ResourceServerSecurityConfigurer resources) {
   resources.resourceId(RESOURCE_ID)//资源 id
           .tokenStore(tokenStore)
           .stateless(true);
  }

 @Override
 public void configure(HttpSecurity http) throws Exception {
  http.csrf().disable()
          .authorizeRequests()
//需要认证的请求
//                .antMatchers("/r/**","/course/**").authenticated()//所有匹配的请求必须经过认证
          .anyRequest().permitAll()    //全部请求放行
  ;
 }

 }
@Configuration
public class TokenConfig {

//对称加密,和认真模块的令牌配置类的密钥相同
    String SIGNING_KEY = "mq123";


//    @Bean
//    public TokenStore tokenStore() {
//        //使用内存存储令牌(普通令牌)
//        return new InMemoryTokenStore();
//    }

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }


}

2.4.6 prueba httpclient, llevar token para acceder al servicio de recursos 

1. Configure temporalmente la ruta del curso para ser autenticado

Para modificar primero el módulo de contenido, la clase de configuración del servicio de recursos ResouceServerConfig, la ruta del curso de configuración debe estar autenticada:

2. Reinicie el servicio de administración de contenido y use httpclient para probar:

1. Acceso para consultar la interfaz del curso según la identificación del curso.

### 查询课程信息
GET http://localhost:63040/content/course/2

devolver:



{
  "error": "unauthorized",
  "error_description": "Full authentication is required to access this resource"
}

De la información devuelta se puede ver que actualmente no existe autenticación.

3. Lo siguiente lleva la interfaz de acceso al token JWT:

1. Solicite el token jwt

Solicite un token en modo contraseña.

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

2. Acceda a la dirección del servicio de recursos con el token jwt

### 携带token访问资源服务
GET http://localhost:63040/content/course/2
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJ6aGFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2NjQzMzM0OTgsImF1dGhvcml0aWVzIjpbInAxIl0sImp0aSI6IjhhM2M2OTk1LWU1ZGEtNDQ1Yy05ZDAyLTEwNDFlYzk3NTkwOSIsImNsaWVudF9pZCI6ImMxIn0.73eNDxTX5ifttGCjwc7xrd-Sbp_mCfcIerI3lGetZto

Agregue Autorización en el encabezado de la solicitud , el contenido es el token de portador y el portador se utiliza para acceder a recursos a través del protocolo oauth2.0.

Si se lleva el token jwt y el jwt es correcto, se puede acceder normalmente al contenido del servicio de recursos.

Si no es correcto, se informará un error de que el token no es válido:

{
  "error": "invalid_token",
  "error_description": "Cannot convert access token to JSON"
}

2.4.7 Prueba de código, la clase SecurityContextHolder obtiene la identidad del usuario

 La ruta del curso de configuración provisional debe estar autenticada

Para modificar primero el módulo de contenido, la clase de configuración del servicio de recursos ResouceServerConfig, la ruta del curso de configuración debe estar autenticada:

La información de identidad del usuario se registra en el token jwt. Cuando el cliente lleva el jwt para acceder al servicio de recursos, el servicio de recursos restaurará el contenido de las dos primeras partes después de la verificación del servicio de recursos para recuperar la información de identidad del usuario, y Coloque la información de identidad del usuario en el contexto SecurityContextHolder, SecurityContext está vinculado al hilo actual para facilitar la obtención de la identidad del usuario.

También tome la interfaz del curso de consulta como ejemplo, ingrese el código de la interfaz del curso de consulta y agregue el código para obtener la identidad del usuario.

@ApiOperation("根据课程id查询课程基础信息")
@GetMapping("/course/{courseId}")
public CourseBaseInfoDto getCourseBaseById(@PathVariable("courseId") Long courseId){
    //取出当前用户身份。底层使用的ThreadLocal,把用户身份信息放到线程里
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    System.out.println(principal);
    return courseBaseInfoService.getCourseBaseInfo(courseId);
}

Nota al realizar la prueba:

1. Primero, especifique el mecanismo de interceptación de seguridad en la configuración del servicio de recursos. Las solicitudes que comienzan con /curso/ deben autenticarse, es decir, las solicitudes a la interfaz /curso/{courseId} deben llevar tokens jwt y aprobar la visa .

2. El servicio de autenticación genera un token jwt y escribe la información de identidad del usuario en el token. Actualmente, la información del usuario está codificada y almacenada temporalmente en la memoria.

como sigue:

@Bean
public UserDetailsService userDetailsService() {
    //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
    manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
    return manager;
}

3. Usamos la información de zhangsan cuando usamos el modo de contraseña para generar el token jwt, por lo que la información de zhangsan se almacena en el token de jwt, por lo que la información de zhangsan debe extraerse en el servicio de recursos.

Después de borrar el contenido anterior, reinicie el servicio de administración de contenido a continuación para rastrear si la identidad del usuario obtenida es correcta.

Autenticación de 3 puertas de enlace

Revisar las responsabilidades de la puerta de enlace

Autenticación: como entrada de microservicio, la puerta de enlace debe verificar si el usuario es elegible para la solicitud e interceptarla en caso contrario.

Enrutamiento y equilibrio de carga: todas las solicitudes deben pasar primero a través de la puerta de enlace, pero la puerta de enlace no procesa el negocio, sino que reenvía la solicitud a un microservicio de acuerdo con ciertas reglas, este proceso se llama enrutamiento. Por supuesto, cuando hay múltiples servicios de destino para el enrutamiento, también se requiere equilibrio de carga.

Limitación actual: cuando el tráfico de solicitudes es demasiado alto, la puerta de enlace liberará la solicitud de acuerdo con la velocidad que el microservicio descendente pueda aceptar, para evitar una presión excesiva del servicio.

3.1 Proceso de certificación 

Hasta ahora, la prueba ha pasado el servicio de autenticación para emitir el token jwt, el cliente lleva el jwt para acceder al servicio de recursos y el servicio de recursos verifica la legitimidad del jwt. Como se muestra abajo:

Si observamos detenidamente este diagrama, falta un componente muy importante en la arquitectura de este proyecto: la puerta de enlace.

Agregue la puerta de enlace y complete el diagrama de flujo de autenticación:

Todas las solicitudes para acceder a microservicios deben pasar a través de la puerta de enlace. La autenticación de la identidad del usuario en la puerta de enlace puede interceptar muchas solicitudes ilegales fuera de los microservicios, lo que se denomina autenticación de puerta de enlace.

Responsabilidades de la puerta de enlace:

1. Mantenimiento de la lista blanca de sitios web

Permita todas las URL que no requieran autenticación.

2. Verificar la legitimidad de jwt.

Además de la lista blanca, el resto es la solicitud que debe ser autenticada. La puerta de enlace debe verificar la legitimidad del jwt. Si el jwt es legal, significa que la identidad del usuario es legal. De lo contrario, significa que la identidad no es legal y se niega a seguir accediendo.

La puerta de enlace no es responsable de la autorización, sólo de la autenticación:

La puerta de enlace no es responsable de la autorización y la operación de autorización para la solicitud se realiza en cada microservicio, porque el microservicio sabe a qué interfaces tiene mejor acceso el usuario.

3.2 [Módulo de puerta de enlace] La puerta de enlace verifica uniformemente jwt y mantiene la lista blanca

Lo siguiente implementa el mantenimiento de la lista blanca de sitios web de autenticación de puerta de enlace y verifica la legitimidad de jwt:

1. Agregar dependencias al proyecto de puerta de enlace.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
</dependency>

2. Importe la clase de configuración al paquete de configuración del proyecto de puerta de enlace.

Estas clases de configuración son comunes:

Filtro de autenticación de puerta de enlace
/**
 * @author Mr.M
 * @version 1.0
 * @description 网关认证过虑器
 * @date 2022/9/27 12:10
 */
@Component
@Slf4j
public class GatewayAuthFilter implements GlobalFilter, Ordered {


    //白名单
    private static List<String> whitelist = null;

    static {
        //加载白名单
        try (
                InputStream resourceAsStream = GatewayAuthFilter.class.getResourceAsStream("/security-whitelist.properties");
        ) {
            Properties properties = new Properties();
            properties.load(resourceAsStream);
            Set<String> strings = properties.stringPropertyNames();
            whitelist= new ArrayList<>(strings);

        } catch (Exception e) {
            log.error("加载/security-whitelist.properties出错:{}",e.getMessage());
            e.printStackTrace();
        }


    }

    @Autowired
    private TokenStore tokenStore;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //请求的url
        String requestUrl = exchange.getRequest().getPath().value();
        AntPathMatcher pathMatcher = new AntPathMatcher();
        //白名单放行
        for (String url : whitelist) {
            if (pathMatcher.match(url, requestUrl)) {
                return chain.filter(exchange);
            }
        }

        //检查token是否存在
        String token = getToken(exchange);
        if (StringUtils.isBlank(token)) {
            return buildReturnMono("没有认证",exchange);
        }
        //判断是否是有效的token
        OAuth2AccessToken oAuth2AccessToken;
        try {
            oAuth2AccessToken = tokenStore.readAccessToken(token);

            boolean expired = oAuth2AccessToken.isExpired();
            if (expired) {
                return buildReturnMono("认证令牌已过期",exchange);
            }
            return chain.filter(exchange);
        } catch (InvalidTokenException e) {
            log.info("认证令牌无效: {}", token);
            return buildReturnMono("认证令牌无效",exchange);
        }

    }

    /**
     * 获取token
     */
    private String getToken(ServerWebExchange exchange) {
        String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (StringUtils.isBlank(tokenStr)) {
            return null;
        }
        String token = tokenStr.split(" ")[1];
        if (StringUtils.isBlank(token)) {
            return null;
        }
        return token;
    }




    private Mono<Void> buildReturnMono(String error, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        String jsonString = JSON.toJSONString(new RestErrorResponse(error));
        byte[] bits = jsonString.getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }


    @Override
    public int getOrder() {
        return 0;
    }
}
Configuración de seguridad
 @EnableWebFluxSecurity
 @Configuration
 public class SecurityConfig {


  //安全拦截配置
  @Bean
  public SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) {

   return http.authorizeExchange()
           .pathMatchers("/**").permitAll()
           .anyExchange().authenticated()
           .and().csrf().disable().build();
  }


 }
Configuración de token
@Configuration
public class TokenConfig {

    String SIGNING_KEY = "mq123";


//    @Bean
//    public TokenStore tokenStore() {
//        //使用内存存储令牌(普通令牌)
//        return new InMemoryTokenStore();
//    }

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }


}

RestErrorRespuesta
/**
 * 错误响应参数包装
 */
public class RestErrorResponse implements Serializable {

    private String errMessage;

    public RestErrorResponse(String errMessage){
        this.errMessage= errMessage;
    }

    public String getErrMessage() {
        return errMessage;
    }

    public void setErrMessage(String errMessage) {
        this.errMessage = errMessage;
    }
}

3. Configure el archivo de lista blanca security-whitelist.properties

El contenido es el siguiente (complementado continuamente)

/auth/**=认证地址
/content/open/**=内容管理公开访问接口
/media/open/**=媒资管理公开访问接口

4. Prueba:

Reinicie el proyecto de puerta de enlace para realizar pruebas.

1) Solicite una ficha

2) Acceder a los servicios de recursos a través de la puerta de enlace.

Accede a los servicios de gestión de contenidos aquí

### 通过网关访问资源服务
GET http://localhost:63010/content/course/2
Authorization: Bearer eyJXxx.eyJhdXxx.lOITXxx

Cuando el token es correcto, se puede acceder al servicio de recursos normalmente. Si la verificación del token falla, el token devuelto no es válido:

{
  "errMessage": "认证令牌无效"
}

5. Antes del desarrollo de la función de autenticación, todas las rutas se liberan temporalmente.

Nota: Una vez depurada la función de autenticación de la puerta de enlace, dado que la función de autenticación aún no se ha desarrollado, cuando la URL de la puerta de enlace de solicitud de front-end no esté en el medio de la lista blanca, se producirá un error de "sin autenticación". Agregue temporalmente todas las configuraciones de versión a la lista blanca, en espera del desarrollo de la función de autenticación . Después de completar, proteja todas las configuraciones de versión.

6. Comente la ruta de autenticación en la clase de configuración en el módulo de contenido.

Dado que la verificación del token se realiza en la puerta de enlace, la legitimidad del token ya no se verifica en el microservicio y la clase ResourceServerConfig del servicio de administración de contenido se modifica para bloquear autenticado().

 @Override
 public void configure(HttpSecurity http) throws Exception {
  http.csrf().disable()
          .authorizeRequests()
//          .antMatchers("/r/**","/course/**").authenticated()//所有/r/**的请求必须认证通过
          .anyRequest().permitAll()
  ;
 }

Supongo que te gusta

Origin blog.csdn.net/qq_40991313/article/details/130093971
Recomendado
Clasificación