Autorización de autenticación Spring Security OAuth2.0---Avanzado

6. OAuth2.0

6.1 Introducción a OAuth2.0

OAuth (autorización abierta) es un estándar abierto que permite a los usuarios autorizar aplicaciones de terceros para acceder a su información almacenada en otro proveedor de servicios sin dar a la aplicación de terceros un nombre de usuario y contraseña o compartir todo el contenido de sus datos. OAuth2.0 es una continuación del protocolo OAuth, pero no es compatible con versiones anteriores de OAuth1.0, lo que significa que OAuth1.0 se elimina por completo. Muchas grandes empresas como Google, Yahoo, Microsoft, etc. han proporcionado servicios de autenticación OAUTH, lo que es suficiente para demostrar que el estándar OAUTH se ha convertido gradualmente en el estándar para la autorización de código abierto.

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

Referencia: https://baike.baidu.com/item/oAuth/7153134?fr=aladdin

Protocolo de autenticación: https://tools.ietf.org/html/rfc6749

Analicemos un ejemplo de autenticación Oauth2 y usemos el ejemplo para comprender el proceso de autenticación del protocolo OAuth2.0. Este ejemplo es el proceso de usar la autenticación WeChat en el sitio web de Angyan Data. La breve descripción de este proceso es la siguiente:

El usuario inicia sesión en el sitio web de Angyan Data a través de la autenticación de WeChat y no necesita registrarse como usuario solo en Angyan Data. ¿Cómo es exitosa la autenticación? El sitio web de datos de Angyan necesita obtener con éxito la información de identidad del usuario de WeChat y considerar que la autenticación del usuario es exitosa, entonces, ¿cómo obtener la información de identidad del usuario de WeChat? El propietario de la información del usuario es el propio usuario, y WeChat necesita el consentimiento del usuario para generar un token para el sitio web de Angyan Data, y el sitio web de Angyan Data puede obtener la información del usuario de WeChat con este token.

1. El cliente solicita autorización de terceros

  • El usuario ingresa a la página de inicio de sesión del programa de datos de Ang Yan, hace clic en el icono de WeChat para iniciar sesión en el sistema con una cuenta de WeChat y el usuario es el propietario de los recursos de su propia información en WeChat. Haga clic en "WeChat" y aparecerá un código QR. En este momento, el usuario escanea el código QR y comienza a autorizar los datos de Ang Yan.

2. El propietario del recurso acepta autorizar al cliente

  • El propietario del recurso escanea el código QR para indicar que acepta autorizar al cliente. WeChat verificará la identidad del propietario del recurso. Después de pasar la verificación, WeChat le preguntará al usuario si autoriza a Angyan Data a acceder a sus datos de WeChat. El usuario hace clic en "Confirmar inicio de sesión" significa que acepta la autorización, y el servidor de autenticación de WeChat emitirá un código de autorización y lo redirigirá al sitio web de Angyan Data.

3. El cliente obtiene el código de autorización y solicita al servidor de autenticación que solicite un token

  • El usuario no puede ver este proceso y la aplicación cliente solicita al servidor de autenticación que lleve el código de autorización.

4. El servidor de autenticación responde con un token al cliente

  • El servidor de autenticación de WeChat verifica el código de autorización solicitado por el cliente y emite un token para el cliente si es legal, y el token es el pasaporte para que el cliente acceda a los recursos.
    El usuario no puede ver este proceso de interacción. Cuando el cliente obtiene el token, el usuario puede ver que el inicio de sesión ha sido exitoso en los datos de Ang Yan.

5. El cliente solicita recursos del servidor de recursos

  • El cliente lleva el token para acceder a los recursos del servidor de recursos. El sitio web de datos de Ang Yan lleva el token para solicitar acceso al servidor de WeChat para obtener la información básica del usuario.

6. El servidor de recursos devuelve el recurso protegido.

  • El servidor de recursos verifica la legitimidad del token y, si es legal, responde al contenido de la información del recurso para el usuario.

El proceso de ejecución detallado de la autenticación y autorización anterior es el siguiente:

inserte la descripción de la imagen aquí

A través del ejemplo anterior, tenemos una comprensión general del proceso de autenticación de OAauth2.0. Veamos el proceso de autenticación de OAuth2.0 a continuación:
citado del protocolo OAauth2.0 https://tools.ietf.org/html/rfc6749

inserte la descripción de la imagen aquí
OAauth2.0 incluye los siguientes roles:

1. Cliente

  • No almacena recursos en sí mismo y necesita solicitar recursos del servidor de recursos a través de la autorización del propietario del recurso, como: cliente de Android, cliente web (extremo del navegador), cliente de WeChat, etc.

2. Propietario del recurso

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

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

  • El proveedor de servicios lo utiliza para autenticar la identidad propiedad del recurso y autorizar el acceso al recurso. Después de una autenticación exitosa, el cliente emitirá un token (access_token) como la credencial para que el cliente acceda al servidor de recursos. Este ejemplo es el servidor de autenticación de WeChat.

4. Servidor de recursos

  • Un servidor que almacena recursos, en este ejemplo, información de usuario almacenada por WeChat.

Ahora hay otra pregunta, ¿puede el proveedor de servicios permitir que cualquier cliente acceda a su servidor de autorización?

La respuesta es no, el proveedor de servicios le dará a la parte de acceso una identidad para las credenciales de acceso: client_id: ID del cliente client_secret: clave secreta del cliente

Por lo tanto, para ser precisos, el servidor de autorización realiza la autenticación y la autorización en dos roles en OAuth2.0, que son el propietario del recurso y el cliente.

6.2, OAuth2 de seguridad en la nube de Spring

6.2.1 Introducción al entorno

Spring-Security-OAuth2 es una implementación de OAuth2, y complementa Spring Security que aprendimos antes. También es muy conveniente para integrarse con el sistema Spring Cloud. A continuación, debemos aprenderlo y finalmente usarlo para realizar nuestro diseño A solución distribuida de autenticación y autorización.

El proveedor de servicios de OAuth2.0 cubre dos servicios, a saber, Servidor de autorización (también llamado Servicio de autenticación) y Servidor de recursos (Servidor de recursos).Al usar Spring Security OAuth2, puede optar por implementarlos en la misma aplicación, también puede optar por crear múltiples servicios de recursos usando el mismo servicio de autorización.

El servicio de autorización (Servidor de autorización) debe incluir funciones como verificar la legitimidad de la terminal de acceso y el usuario de inicio de sesión y emitir tokens. El punto final de solicitud para el token es implementado por el controlador Spring MVC. Lo siguiente es lo que debe implementarse para configurar un punto final del servicio de autenticación:

  • AuthorizationEndpoint sirve solicitudes de autenticación. URL predeterminada: /oauth/autorizar.
  • TokenEndpoint atiende solicitudes de tokens de acceso. URL predeterminada: /oauth/token.

Resource Server (Servidor de recursos) debe incluir funciones de protección de recursos, interceptar solicitudes ilegales, analizar y autenticar tokens en solicitudes, etc. Los siguientes filtros se utilizan para implementar los servicios de recursos de OAuth 2.0:

  • OAuth2AuthenticationProcessingFilter se usa para analizar y autenticar el token de identidad proporcionado por la solicitud.

A continuación, cree el servicio de autorización uaa (también llamado servicio de autenticación) y ordene el servicio de recursos respectivamente.
inserte la descripción de la imagen aquí

El proceso de autenticación es el siguiente:

  • 1. El cliente solicita el servicio de autorización de la UAA para su autenticación.
  • 2. Después de pasar la autenticación, UAA emitirá un token.
  • 3. El cliente porta el token Token para solicitar servicios de recursos.
  • 4. El servicio de recursos verifica la legitimidad del token y devuelve la información del recurso si es legal.

6.2.2 Construcción del entorno

6.2.2.1, proyecto principal

Cree un proyecto maven como proyecto principal y las dependencias son las siguientes:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.test.security</groupId>
    <artifactId>distributed-security</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>pom</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>

            <dependency>
                <groupId>javax.interceptor</groupId>
                <artifactId>javax.interceptor-api</artifactId>
                <version>1.2</version>
            </dependency>

            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.47</version>
            </dependency>

            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.0</version>
            </dependency>

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>


            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-jwt</artifactId>
                <version>1.0.10.RELEASE</version>
            </dependency>


            <dependency>
                <groupId>org.springframework.security.oauth.boot</groupId>
                <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                <version>2.1.3.RELEASE</version>
            </dependency>


        </dependencies>
    </dependencyManagement>



    <build>
        <finalName>${project.name}</finalName>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>**/*</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <!--<plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>-->

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>utf-8</encoding>
                    <useDefaultDelimiters>true</useDefaultDelimiters>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

6.2.2.2 Crear proyecto de servicio de autorizaciones UAA

6.2.2.2.1 Crear seguridad-distribuida-uaa

Cree la seguridad distribuida-uaa como un proyecto de servicio de autorización, las dependencias son las siguientes:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>distributed-security</artifactId>
        <groupId>com.test.security</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>distributed-security-uaa</artifactId>

    <dependencies>

        <dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
         </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

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

        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
        </dependency>

        <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.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
        </dependency>

        <dependency>
            <groupId>javax.interceptor</groupId>
            <artifactId>javax.interceptor-api</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>


        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>


    </dependencies>

</project>
6.2.2.2.2, clase de inicio

Este proyecto se desarrolla usando SpringBoot, y cada proyecto escribe una clase de inicio:

@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableFeignClients(basePackages = {
    
    "com.test.security.distributed.uaa"})
public class UAAServer {
    
    
	public static void main(String[] args) {
    
    
		SpringApplication.run(UAAServer.class, args);
	}
}
6.2.2.2.3, archivo de configuración

Crear application.properties en recursos

spring.application.name=uaa-service
server.port=53020
spring.main.allow-bean-definition-overriding = true

logging.level.root = debug
logging.level.org.springframework.web = info

spring.http.encoding.enabled = true
spring.http.encoding.charset = UTF-8
spring.http.encoding.force = true
server.tomcat.remote_ip_header = x-forwarded-for
server.tomcat.protocol_header = x-forwarded-proto
server.use-forward-headers = true
server.servlet.context-path = /uaa

spring.freemarker.enabled = true
spring.freemarker.suffix = .html
spring.freemarker.request-context-attribute = rc
spring.freemarker.content-type = text/html
spring.freemarker.charset = UTF-8
spring.mvc.throw-exception-if-no-handler-found = true
spring.resources.add-mappings = false

spring.datasource.url = jdbc:mysql://localhost:3306/user_db?useUnicode=true
spring.datasource.username = root
spring.datasource.password = mysql
spring.datasource.driver-class-name = com.mysql.jdbc.Driver

eureka.client.serviceUrl.defaultZone = http://localhost:53000/eureka/
eureka.instance.preferIpAddress = true
eureka.instance.instance-id = ${
    
    spring.application.name}:${
    
    spring.cloud.client.ip-address}:${
    
    spring.application.instance_id:${
    
    server.port}}
management.endpoints.web.exposure.include = refresh,health,info,env

feign.hystrix.enabled = true
feign.compression.request.enabled = true
feign.compression.request.mime-types[0] = text/xml
feign.compression.request.mime-types[1] = application/xml
feign.compression.request.mime-types[2] = application/json
feign.compression.request.min-request-size = 2048
feign.compression.response.enabled = true

6.2.2.3 Crear un proyecto de servicio de recursos de pedido

Este proyecto es un proyecto de servicio de pedidos y el acceso a los recursos de este proyecto requiere autenticación.
El propósito de este proyecto es principalmente probar la función de autenticación y autorización, por lo que no involucra negocios relacionados con la gestión de pedidos.

6.2.2.3.1 Crear proyecto de orden de seguridad distribuida
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>distributed-security</artifactId>
        <groupId>com.test.security</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>distributed-security-order</artifactId>
    <dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <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>javax.interceptor</groupId>
            <artifactId>javax.interceptor-api</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>


    </dependencies>

</project>
6.2.2.3.2, clase de inicio
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServer {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(OrderServer.class, args);
    }
}
6.2.2.3.3, archivo de configuración

Crear aplicación.propiedades en recursos

spring.application.name=order-service
server.port=53021
spring.main.allow-bean-definition-overriding = true

logging.level.root = debug
logging.level.org.springframework.web = info
spring.http.encoding.enabled = true
spring.http.encoding.charset = UTF-8
spring.http.encoding.force = true
server.tomcat.remote_ip_header = x-forwarded-for
server.tomcat.protocol_header = x-forwarded-proto
server.use-forward-headers = true
server.servlet.context-path = /order


spring.freemarker.enabled = true
spring.freemarker.suffix = .html
spring.freemarker.request-context-attribute = rc
spring.freemarker.content-type = text/html
spring.freemarker.charset = UTF-8
spring.mvc.throw-exception-if-no-handler-found = true
spring.resources.add-mappings = false


eureka.client.serviceUrl.defaultZone = http://localhost:53000/eureka/
eureka.instance.preferIpAddress = true
eureka.instance.instance-id = ${
    
    spring.application.name}:${
    
    spring.cloud.client.ip-address}:${
    
    spring.application.instance_id:${
    
    server.port}}
management.endpoints.web.exposure.include = refresh,health,info,env

feign.hystrix.enabled = true
feign.compression.request.enabled = true
feign.compression.request.mime-types[0] = text/xml
feign.compression.request.mime-types[1] = application/xml
feign.compression.request.mime-types[2] = application/json
feign.compression.request.min-request-size = 2048
feign.compression.response.enabled = true

6.2.3 Configuración del servidor de autorizaciones

6.2.3.1, Habilitar servidor de autorización

Puede usar la anotación @EnableAuthorizationServer y heredar AuthorizationServerConfigurerAdapter para configurar el servidor de autorización OAuth2.0.

Cree AuthorizationServer bajo el paquete Config:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    
    
	//略...
}

AuthorizationServerConfigurerAdapter requiere la configuración de las siguientes clases, que son objetos de configuración independientes creados por Spring, que Spring pasará a AuthorizationServerConfigurer para la configuración.

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 {
    
    }
}
  • ClientDetailsServiceConfigurer: se utiliza para configurar el servicio de detalles del cliente (ClientDetailsService). La información de detalles del cliente se inicializa aquí. Puede escribir la información de detalles del cliente aquí o almacenar y recuperar la información de detalles a través de la base de datos.

  • AuthorizationServerEndpointsConfigurer: se usa para configurar puntos finales de acceso y servicios de token para tokens.

  • AuthorizationServerSecurityConfigurer: se utiliza para configurar las restricciones de seguridad del extremo del token.

6.2.3.2 Configurar detalles del cliente

ClientDetailsServiceConfigurer puede usar memoria o JDBC para implementar el servicio de detalles del cliente (ClientDetailsService), ClientDetailsService es responsable de encontrar ClientDetails y ClientDetails tiene varios atributos importantes, como se indica a continuación:

  • clientId: (obligatorio) el Id utilizado para identificar al cliente.

  • secreto: (requiere clientes de confianza) el código de seguridad del cliente, si lo tiene.

  • alcance: se utiliza para limitar el alcance de acceso del cliente, si está vacío (predeterminado), entonces el cliente tiene todo el alcance de acceso.

  • AuthorizedGrantTypes: tipos de concesión que este cliente puede usar, el valor predeterminado está vacío.

  • autoridades: Las autoridades que este cliente puede usar (basadas en las autoridades de Spring Security).

  • redirectUris: URL de devolución de llamada. El servicio de autorización enviará información relacionada con este cliente a la dirección de devolución de llamada.

Los detalles del cliente (Client Details) se pueden actualizar cuando la aplicación se está ejecutando, accediendo al servicio de almacenamiento subyacente (por ejemplo, almacenando los detalles del cliente en una tabla de base de datos relacional, puede usar JdbcClientDetailsService) o implementando la interfaz ClientRegistrationService usted mismo (al mismo tiempo). tiempo, también puede implementar la interfaz ClientDetailsService) para administrar.

Usamos la memoria temporalmente para almacenar los detalles del cliente, y la configuración es la siguiente:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    
    
	
	//配置客户端详细信息服务
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    
    
		// clients.withClientDetails(clientDetailsService);
		clients.inMemory()// 使用in‐memory存储
			.withClient("c1")// client_id
			.secret(new BCryptPasswordEncoder().encode("secret"))//客户端密钥
			.resourceIds("res1")//资源列表
			.authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token")// 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials
			.scopes("all")// 允许的授权范围
			.autoApprove(false)//false代表跳转到授权页面
			//加上验证回调地址
			.redirectUris("http://www.baidu.com");
	}
}

6.2.3.3, token de gestión

La interfaz de AuthorizationServerTokenServices define algunas operaciones para que pueda realizar la administración necesaria en el token. El token se puede usar para cargar información de identidad, que contiene los permisos relevantes del token. Puede crear la implementación de la interfaz AuthorizationServerTokenServices usted mismo, necesita heredar la clase DefaultTokenServices, que contiene algunas implementaciones útiles, y puede usarla para modificar el formato del token y el almacenamiento del token. De forma predeterminada, cuando intenta crear un token, se llena con un valor aleatorio.Esta clase hace casi todo por usted, excepto que el token persistente se delega a una interfaz TokenStore. Y la interfaz TokenStore tiene una implementación predeterminada, que es InMemoryTokenStore.Como se llama, todos los tokens se almacenan en la memoria. Además de usar esta clase, también puede usar otras implementaciones predefinidas. Hay varias versiones a continuación, todas las cuales implementan la interfaz TokenStore:

  • InMemoryTokenStore: la implementación de esta versión se adopta de forma predeterminada.Puede funcionar perfectamente en un solo servidor (es decir, cuando la presión del acceso concurrente no es alta y no se respaldará cuando falla), la mayoría de los proyectos se pueden use esta versión de la implementación para probarla, puede usarla para administrar durante el desarrollo, es más fácil de depurar porque no se guardará en el disco.

  • JdbcTokenStore: esta es una implementación basada en JDBC y el token se almacenará en una base de datos relacional. Al usar esta versión de la implementación, puede compartir información de token entre diferentes servidores. Al usar esta versión, preste atención a agregar la dependencia "spring-jdbc" a su classpath.

  • JwtTokenStore: el nombre completo de esta versión es JSON Web Token (JWT), que puede codificar datos relacionados con el token (por lo tanto, para los servicios de backend, no es necesario almacenarlo, lo que será una gran ventaja), pero tiene una desventaja. , es que será muy difícil revocar un token autorizado, por lo que generalmente se usa para tratar con un token de corta duración y revocar tokens de actualización (refresh_token). Otra desventaja es que el espacio ocupado por este token será relativamente grande si agrega más información de credenciales de usuario. JwtTokenStore no almacena ningún dato, pero desempeña el mismo papel que DefaultTokenServices en la conversión de valores de token e información de autorización.

1. Defina TokenConfig
Defina TokenConfig en el paquete de configuración Por el momento, usamos InMemoryTokenStore para generar un token común.

@Configuration
public class TokenConfig {
    
    
	@Bean
	public TokenStore tokenStore() {
    
    
		//内存方式生成普通令牌
		return new InMemoryTokenStore();
	}
}

2. Definir AuthorizationServerTokenServices
Definir AuthorizationServerTokenServices en AuthorizationServer

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    
    
	
	@Autowired
	private TokenStore tokenStore;
	
	@Autowired
	private ClientDetailsService clientDetailsService;
	
	//令牌管理服务
	@Bean
	public AuthorizationServerTokenServices tokenService() {
    
    
		DefaultTokenServices service=new DefaultTokenServices();
		//客户端信息服务
		service.setClientDetailsService(clientDetailsService);
		//是否刷新令牌
		service.setSupportRefreshToken(true);
		//令牌存储策略
		service.setTokenStore(tokenStore);
		// 令牌默认有效期2小时
		service.setAccessTokenValiditySeconds(7200); 
		// 刷新令牌默认有效期3天
		service.setRefreshTokenValiditySeconds(259200); 
		return service;
	}
}

6.2.3.4, configuración del punto final de acceso al token

La instancia de AuthorizationServerEndpointsConfigurer de este objeto puede completar el servicio de token y la configuración del punto final del token.

Configurar tipos de concesión
AuthorizationServerEndpointsConfigurer determina los tipos de concesión admitidos configurando las siguientes propiedades:

  • AuthenticationManager: administrador de autenticación, cuando elija el tipo de autorización de la contraseña del propietario del recurso (contraseña), configure esta propiedad para inyectar un objeto AuthenticationManager.

  • userDetailsService: si configura esta propiedad, significa que tiene una implementación de su propia interfaz UserDetailsService, o puede configurar esto en el dominio global (como el objeto de configuración GlobalAuthenticationManagerConfigurer), cuando configura esto, entonces "refresh_token" significa que el proceso de actualización del modo de tipo de autorización de token incluirá una verificación para garantizar que la cuenta siga siendo válida, si la desactiva.

  • AuthorizationCodeServices: este atributo se utiliza para establecer el servicio de código de autorización (es decir, el objeto de instancia de AuthorizationCodeServices), que se utiliza principalmente en el modo de tipo de código de autorización "authorization_code".

  • implícitaGrantService: este atributo se utiliza para establecer el modo de concesión implícita, que se utiliza para gestionar el estado del modo de concesión implícita.

  • tokenGranter: cuando configura esto (es decir, la implementación de la interfaz TokenGranter), usted controlará completamente la autorización y los atributos anteriores se ignorarán. Este atributo generalmente se usa con fines de expansión, es decir, el estándar Solo cuando los cuatro modos de autorización ya no pueden satisfacer sus necesidades, ¿considerará usar esto?

Configure la URL del punto final de autorización (URL de punto final):
AuthorizationServerEndpointsConfigurer Este objeto de configuración tiene un método llamado pathMapping() para configurar el enlace de la URL del punto final, que tiene dos parámetros:

  • El primer parámetro: tipo de cadena, el enlace predeterminado de esta URL de punto final.
  • El segundo parámetro: tipo de cadena, desea reemplazar el enlace URL.

Todos los parámetros anteriores serán cadenas que comiencen con el carácter "/". Los enlaces URL predeterminados del marco se enumeran a continuación, que se pueden usar como el primer parámetro del método pathMapping():

  • /oauth/authorize: Extremo de autorización.
  • /oauth/token: punto final del token.
  • /oauth/confirm_access: Extremo de envío de autorización de confirmación de usuario.
  • /oauth/error: Extremo de información de error del servicio de autorización.
  • /oauth/check_token: punto final de resolución de token para el acceso al servicio de recursos.
  • /oauth/token_key: un punto final que proporciona la clave pública, si usa tokens JWT.

Cabe señalar que la URL del punto final de autorización debe estar protegida por Spring Security y solo los usuarios autorizados pueden acceder a ella.Configure el punto final de acceso al token en AuthorizationServer

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    
    

	@Autowired
	private AuthorizationCodeServices authorizationCodeServices;
	
	@Autowired
	private AuthenticationManager authenticationManager;
	
	//令牌访问端点
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    
    
		endpoints
			.authenticationManager(authenticationManager)//密码模式需要
			.authorizationCodeServices(authorizationCodeServices)//授权码模式需要
			.tokenServices(tokenService())//令牌管理服务
			.allowedTokenEndpointRequestMethods(HttpMethod.POST);//允许post提交
	}
	
	@Bean
	public AuthorizationCodeServices authorizationCodeServices() {
    
    
		 //设置授权码模式的授权码如何存取,暂时采用内存方式
		return new InMemoryAuthorizationCodeServices();
	}
}

6.2.3.5 Restricciones de seguridad en los extremos del token

AuthorizationServerSecurityConfigurer: se utiliza para configurar las restricciones de seguridad de Token Endpoint, que se configura en AuthorizationServer de la siguiente manera.

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    
    

	//令牌访问端点安全策略
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security){
    
    
		security
			.tokenKeyAccess("permitAll()") // oauth/token_key这个endpoint当使用JwtToken且使用非对称加密时,资源服务用于获取公钥而开放的,这里指这个endpoint完全公开。
			.checkTokenAccess("permitAll()") // oauth/check_token这个endpoint完全公开
			.allowFormAuthenticationForClients();//允许表单认证
	}
}

Resumen de la configuración del servicio de autorización: la configuración del servicio de autorización se divide en tres partes, que se pueden asociar con la memoria.

  • Dado que la autenticación debe completarse, primero debe saber de dónde se lee la información del cliente, por lo que es necesario configurar los detalles del cliente.

  • Dado que se va a emitir el token, es necesario definir el punto final relevante del token, cómo acceder al token y qué tipos de tokens admite el cliente.

  • Dado que algunos puntos finales están expuestos, se pueden definir algunas restricciones de seguridad para estos puntos finales.

6.2.3.6, configuración de seguridad web

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    
	@Bean
	public PasswordEncoder passwordEncoder() {
    
    
		return new BCryptPasswordEncoder();
	}
	
	//认证管理器
	@Bean
	public AuthenticationManager authenticationManagerBean() throws Exception {
    
    
		return super.authenticationManagerBean();
	}

	//安全拦截机制(最重要)
	@Override
	protected void configure(HttpSecurity http) throws Exception {
    
    
		http.csrf().disable()
			.authorizeRequests()
			.antMatchers("/r/r1").hasAnyAuthority("p1")
			.antMatchers("/login*").permitAll()
			.anyRequest().authenticated()
			.and()
			.formLogin();
	}
}

6.2.4 Modo código de autorización

6.2.4.1 Introducción al Modo Código de Autorización

La siguiente figura es el diagrama de interacción del modo de código de autorización:

inserte la descripción de la imagen aquí

(1) El propietario del recurso abre el cliente, y el cliente solicita autorización al propietario del recurso, y redirige el navegador al servidor de autorización, y la información de identidad del cliente se adjuntará al redirigir. como:

/uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com

La lista de parámetros es la siguiente:

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

(2) El navegador muestra una página de autorización para el servidor de autorización, y luego el usuario acepta la autorización.
(3) El servidor de autorización envía el código de autorización (AuthorizationCode) al cliente a través del navegador (a través de redirect_uri).
(4) El cliente solicita al servidor de autorización un access_token con el código de autorización, y la solicitud es la siguiente:

/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=5PgfcD&redirect_uri=http://www.baidu.com

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_autorización, indicando 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 usarlo solo una vez, y deberá volver a solicitarlo.
  • redirect_uri: la URL de redireccionamiento al solicitar el código de autorización debe ser coherente con el redirect_uri utilizado al solicitar el código de autorización.

(5) El servidor de autorización devuelve el token (access_token)

Este modo es el más seguro de los cuatro modos. Generalmente se usa cuando el cliente es una aplicación de servidor web o una aplicación nativa de terceros para llamar a los servicios de recursos. Porque en este modo, el access_token no pasará por el navegador o la aplicación móvil, sino que se intercambiará directamente desde el servidor, lo que minimiza el riesgo de fuga de tokens.

6.2.4.2 Prueba

Página de autenticación de acceso del navegador:

http://localhost:53020/uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com

Luego ingrese la cuenta y la contraseña simuladas e inicie sesión para ingresar a la página de autorización:

Después de confirmar la autorización, el navegador redirigirá a la ruta especificada (web_server_redirect_uri en la tabla oauth_client_details) y agregará el código de verificación?code=DB2mFj (diferente cada vez), y finalmente usará el código de verificación para obtener el token.

inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

6.2.5 Modo simplificado

6.2.5.1 Introducción al modo simplificado

La siguiente figura es un diagrama de interacción de modo simplificado:

inserte la descripción de la imagen aquí

(1) El propietario del recurso abre el cliente, y el cliente solicita autorización al propietario del recurso, y redirige el navegador al servidor de autorización, y la información de identidad del cliente se adjuntará al redirigir. como:

/uaa/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com

La descripción del parámetro es la misma que el modo de código de autorización, preste atención a response_type=token, lo que indica que es un modo simplificado.

(2) El navegador muestra una página de autorización para el servidor de autorización, y luego el usuario acepta la autorización.
(3) El servidor de autorización almacena el código de autorización y el token (access_token) en el fragmento del uri de redirección en forma de Hash y lo envía al navegador.

Nota: Fragmento se utiliza principalmente para identificar un determinado recurso en el recurso identificado por URI. Utilice (#) al final de URI como el comienzo de fragmento, donde # no pertenece al valor de fragmento. Por ejemplo, L18 en el URI de https://domain/index#L18 es el valor de fragmento. Solo necesita saber que js puede obtener fragmentos respondiendo a los cambios en la barra de direcciones del navegador.

En general, el modo simplificado se usa para aplicaciones de una sola página de terceros sin un lado del servidor, porque sin un lado del servidor, los códigos de autorización no se pueden recibir.

6.2.5.2 Prueba

Página de autenticación de acceso del navegador:

http://localhost:53020/uaa/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com

Luego ingrese la cuenta y la contraseña simuladas e inicie sesión para ingresar a la página de autorización:

Después de confirmar la autorización, el navegador redirigirá a la ruta especificada (web_server_redirect_uri en la tabla oauth_client_details) y la almacenará en el fragmento del uri de redirección en forma de Hash, como:

http://aa.bb.cc/receive#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbn...

6.2.6, modo de contraseña

6.2.6.1 Introducción al Modo Código de Autorización

La siguiente figura es el diagrama de interacción del modo de contraseña:

inserte la descripción de la imagen aquí

(1) El propietario del recurso envía el nombre de usuario y la contraseña al cliente
(2) El cliente toma el nombre de usuario y la contraseña del propietario del recurso para solicitar un token (access_token) del servidor de autorización, y la solicitud es la siguiente:

/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=password&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
  • username: Nombre de usuario del propietario del recurso.
  • contraseña: Contraseña del propietario del recurso.

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

Este modo es muy simple, pero significa que la información confidencial del usuario se filtra directamente al cliente, por lo que esto significa que este modo solo se puede usar cuando el cliente es desarrollado por nosotros mismos. Por lo tanto, el modo de contraseña generalmente se usa para la aplicación nativa propia o la aplicación de una sola página propia desarrollada por nosotros mismos.

6.2.6.2 Prueba

POST http://localhost:53020/uaa/oauth/token

inserte la descripción de la imagen aquí

6.2.7, modo cliente

6.2.7.1, introducción al modo cliente

inserte la descripción de la imagen aquí

(1) El cliente envía su propia información de identidad al servidor de autorización y solicita un token (access_token)
(2) Después de confirmar que la identidad del cliente es correcta, el cliente envía el token (access_token) al cliente y la solicitud es como sigue:

/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials

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 client_credentials para indicar el modo de cliente

Este modo es el modo más conveniente pero menos seguro. Esto requiere que confiemos completamente en el cliente, y el cliente mismo también está seguro. Por lo tanto, este modo se usa generalmente para proporcionarnos servicios del lado del servidor de plena confianza. Por ejemplo, el sistema asociado está conectado para extraer un conjunto de información del usuario.

6.2.7.2 Prueba

inserte la descripción de la imagen aquí

6.2.8 Prueba de servicio de recursos

6.2.8.1, configuración del servidor de recursos

@EnableResourceServer está anotado en una clase de configuración @Configuration, y el objeto de configuración ResourceServerConfigurer debe usarse para la configuración (puede optar por heredar de ResourceServerConfigurerAdapter y anular el método, el parámetro es una instancia de este objeto), las siguientes son algunas propiedades configurables :

ResourceServerSecurityConfigurer incluye principalmente:

  • tokenServices: una instancia de la clase ResourceServerTokenServices, utilizada para implementar servicios de token.
  • tokenStore: una instancia de la clase TokenStore, que especifica cómo acceder al token, opcional con la configuración de tokenServices
  • resourceId: El ID de este servicio de recursos, este atributo es opcional, pero se recomienda configurarlo y verificarlo en el servicio de autorización.
  • Otros atributos extendidos, como el extractor de token tokenExtractor, se utilizan para extraer el token en la solicitud.

La configuración de HttpSecurity es similar a Spring Security:

  • El comparador de solicitudes se utiliza para establecer la ruta del recurso que debe protegerse. De forma predeterminada, son todas las rutas del servicio de recursos protegidos.
  • Establezca reglas de acceso para recursos protegidos a través de http.authorizeRequests()
  • Otras reglas de protección de permisos personalizados se configuran a través de HttpSecurity.

La anotación @EnableResourceServer agrega automáticamente una cadena de filtro de tipo OAuth2AuthenticationProcessingFilter

Escribir ResourceServerConfig:

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
    
    
	
	public static final String RESOURCE_ID = "res1";
	
	@Override
	public void configure(ResourceServerSecurityConfigurer resources) {
    
    
		resources.resourceId(RESOURCE_ID)
			.tokenServices(tokenService())
			.stateless(true);
	}
	
	@Override
	public void configure(HttpSecurity http) throws Exception {
    
    
		http
			.authorizeRequests()
			.antMatchers("/**").access("#oauth2.hasScope('all')")
			.and().csrf().disable()
			.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
	}
}

6.2.8.2, Verificar token

ResourceServerTokenServices es la otra mitad del servicio de autorización. Si su servicio de autorización y el servicio de recursos están en la misma aplicación, puede usar DefaultTokenServices. De esta manera, no tiene que preocuparse por la consistencia de implementar todas las interfaces necesarias. Si su servidor de recursos está separado, debe asegurarse de que haya un ResourceServerTokenServices coincidente proporcionado por el servicio de autorización, que sabe cómo decodificar el token.

Método de análisis de tokens: use DefaultTokenServices para configurar los métodos de almacenamiento, decodificación y análisis de tokens localmente en el servidor de recursos.Use el servidor de recursos de RemoteTokenServices para decodificar tokens a través de solicitudes HTTP y solicite el punto final del servidor de autorizaciones /oauth/check_token cada vez.

Para usar el punto final /oauth/check_token del servicio de autorización, debe exponer este punto final en el servicio de autorización para que el servicio de recursos pueda acceder a él. Esto se ha mencionado en nuestra configuración del servicio de autorización. El siguiente es un ejemplo. En este ejemplo, estamos en Dos puntos finales /oauth/check_token y /oauth/token_key están configurados en el servicio de autorización:

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    
    
	security
		.tokenKeyAccess("permitAll()")// /oauth/token_key 安全配置
		.checkTokenAccess("permitAll()") // /oauth/check_token 安全配置
}

Configure RemoteTokenServices en el servicio de recursos, configure en ResourceServerConfig:

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
    
    
	
	//资源服务令牌解析服务
	@Bean
	public ResourceServerTokenServices tokenService() {
    
    
		//使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
		RemoteTokenServices service=new RemoteTokenServices();
		service.setCheckTokenEndpointUrl("http://localhost:53020/uaa/oauth/check_token");
		service.setClientId("c1");
		service.setClientSecret("secret");
		return service;
	}
	
	@Override
	public void configure(ResourceServerSecurityConfigurer resources) {
    
    
		resources.resourceId(RESOURCE_ID)
			.tokenServices(tokenService())
			.stateless(true);
	}
}

6.2.8.3 Recursos de escritura

Escriba OrderController en el paquete del controlador, que representa la clase de acceso de los recursos de pedido:

@RestController
public class OrderController {
    
    
	@GetMapping(value = "/r1")
	@PreAuthorize("hasAnyAuthority('p1')")
	public String r1(){
    
    
		return "访问资源1";
	}
}

6.2.8.4, Agregar control de acceso de seguridad

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    
	//安全拦截机制(最重要)
	@Override
	protected void configure(HttpSecurity http) throws Exception {
    
    
		http.csrf().disable()
			.authorizeRequests()
			// .antMatchers("/r/r1").hasAuthority("p2")
			// .antMatchers("/r/r2").hasAuthority("p2")
			.antMatchers("/r/**").authenticated()//所有/r/**的请求必须认证通过
			.anyRequest().permitAll();//除了/r/**,其它的请求可以访问
		
	}
}

6.2.8.5 Prueba

1. Solicitar un token
Aquí usamos el método de contraseña
2. Solicitar recursos
De acuerdo con los requisitos del protocolo oauth2.0, los recursos solicitados deben llevar un token, de la siguiente manera:
El nombre del parámetro del token es: Autorización, y el el valor es: valor del token del portador

inserte la descripción de la imagen aquí

6.3 Ficha JWT

6.3.1 Introducción a JWT

A través de las pruebas anteriores, encontramos que cuando el servicio de recursos y el servicio de autorización no están juntos, el servicio de recursos usa RemoteTokenServices para solicitar de forma remota el servicio de autorización para verificar el token, y si el volumen de acceso es grande, el rendimiento del sistema se verá afectado. Ser afectado.
Para resolver el problema anterior:

El problema anterior se puede resolver utilizando el formato JWT del token. Después de que el usuario se autentique, 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 recurso. El servicio de recursos se basa en lo acordado El algoritmo completa la verificación del token por sí mismo, sin solicitar al servicio de autenticación que complete la autorización cada vez.

6.3.1.1 ¿Qué es JWT?

JSON Web Token (JWT) es un estándar abierto de la industria (RFC 7519), que define un formato de protocolo breve e independiente para transferir objetos JSON entre dos partes en comunicación, y la información transferida se puede verificar mediante firma digital confiable. JWT se puede firmar utilizando el algoritmo HMAC o el par de claves pública/privada RSA para evitar la manipulación.

Sitio web oficial: https://jwt.io/

Estándar: https://tools.ietf.org/html/rfc7519

Ventajas de los tokens JWT:

  • 1) jwt se basa en json, que es muy conveniente para analizar.
  • 2) El contenido enriquecido se puede personalizar en el token, que es fácil de expandir.
  • 3) A través del algoritmo de cifrado asimétrico y la tecnología de firma digital, JWT evita la manipulación y tiene una alta seguridad.
  • 4) Los servicios de recursos usan JWT para completar la autorización sin depender de los servicios de autenticación.

defecto:

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

6.3.1.2, Estructura del token JWT

Construya una base para tokens jwt personalizados aprendiendo la estructura del token JWT.
El token JWT consta de tres partes, cada parte está separada por un punto (.), por ejemplo: xxxxx.yyyyy.zzzzz

6.3.1.2.1、Encabezado

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

Por ejemplo:

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

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

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

6.3.1.2.2, 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 preparados proporcionados por jwt, como: iss (emisor), exp (marca de tiempo de caducidad) , 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.

Por ejemplo:

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

La tercera parte es la firma, que se utiliza para evitar que se altere el contenido de 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.

Por 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.

6.3.2, configurar el servicio de token JWT

Configure el servicio de token jwt en uaa para generar un token en formato jwt.
1. Configuración del token

@Configuration
public class TokenConfig {
    
    

	private String SIGNING_KEY = "uaa123";
	
	@Bean
	public TokenStore tokenStore() {
    
    
		return new JwtTokenStore(accessTokenConverter());
	}
	
	@Bean
	public JwtAccessTokenConverter accessTokenConverter() {
    
    
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
		converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
		return converter;
	}
}

2. Definir el servicio de token JWT

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    
    

	@Autowired
	private JwtAccessTokenConverter accessTokenConverter;

	//令牌管理服务
    @Bean
    public AuthorizationServerTokenServices tokenService() {
    
    
        DefaultTokenServices service=new DefaultTokenServices();
        service.setClientDetailsService(clientDetailsService);//客户端详情服务
        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;
    }

6.3.3 Generar token jwt

inserte la descripción de la imagen aquí

6.3.4 Verificar token jwt

El servicio de recursos debe tener el mismo servicio de firma y token que el servicio de autorización:
1. Copie la clase TokenConfig en el servicio de autorización al servicio de recurso
2. Proteja la clase de servicio de token original del servicio de recurso

@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
    
    

    public static final String RESOURCE_ID = "res1";

    @Autowired
    TokenStore tokenStore;

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

6.4 Mejorar la configuración del entorno

Hasta ahora, la información del cliente y el código de autorización todavía se almacenan en la memoria. En el entorno de producción, se almacenarán en la base de datos. La configuración del entorno se mejora de la siguiente manera:

6.4.1 Crear tabla

Cree la siguiente tabla en user_db:

DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
	`client_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '客户端标识',
	`resource_ids` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '接入资源列表',
	`client_secret` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端秘钥',
	`scope` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
	`authorized_grant_types` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
	`web_server_redirect_uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
	`authorities` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
	`access_token_validity` int(11) NULL DEFAULT NULL,
	`refresh_token_validity` int(11) NULL DEFAULT NULL,
	`additional_information` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
	`create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
	`archived` tinyint(4) NULL DEFAULT NULL,
	`trusted` tinyint(4) NULL DEFAULT NULL,
	`autoapprove` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
	 PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '接入客户端信息' ROW_FORMAT = Dynamic;

INSERT INTO `oauth_client_details` VALUES ('c1', 'res1','$2a$10$NlBC84MVb7F95EXYTXwLneXgCca6/GipyWR5NHm8K0203bSQMLpvm', 'ROLE_ADMIN,ROLE_USER,ROLE_API','client_credentials,password,authorization_code,implicit,refresh_token', 'http://www.baidu.com',NULL, 7200, 259200, NULL, '2019‐09‐09 16:04:28', 0, 0, 'false');
INSERT INTO `oauth_client_details` VALUES ('c2', 'res2','$2a$10$NlBC84MVb7F95EXYTXwLneXgCca6/GipyWR5NHm8K0203bSQMLpvm', 'ROLE_API','client_credentials,password,authorization_code,implicit,refresh_token', 'http://www.baidu.com',NULL, 31536000, 2592000, NULL, '2019‐09‐09 21:48:51', 0, 0, 'false');

La tabla oauth_code, utilizada por Spring Security OAuth2, se utiliza para almacenar códigos de autorización:

DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
	`create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
	`code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
	`authentication` blob NULL,
	 INDEX `code_index`(`code`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

6.4.2 Configurar el servicio de autorización

(1) Modificar AuthorizationServer:
ClientDetailsService y AuthorizationCodeServices leen datos de la base de datos.

//将客户端信息存储到数据库
@Bean
public ClientDetailsService clientDetailsService(DataSource dataSource) {
    
    
    ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
    ((JdbcClientDetailsService) clientDetailsService).setPasswordEncoder(passwordEncoder);
    return clientDetailsService;
}

//配置令牌(token)的访问端点
@Bean
public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
    
    
   return new JdbcAuthorizationCodeServices(dataSource);//设置授权码模式的授权码如何存取
}

7. Spring Security implementa la autorización del sistema distribuido

7.1 Análisis de la demanda

inserte la descripción de la imagen aquí

  • 1. El servicio de autenticación UAA es responsable de la autenticación y autorización.

  • 2. Todas las solicitudes llegan al microservicio a través de la puerta de enlace.

  • 3. La puerta de enlace es responsable de autenticar al cliente y reenviar la solicitud

  • 4. La puerta de enlace analiza el token y lo pasa al microservicio, y el microservicio lo autoriza.

7.1.1, código de servicio de autenticación UAA

Servidor de autorización

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    
    

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Autowired
    PasswordEncoder passwordEncoder;

    //将客户端信息存储到数据库
    @Bean
    public ClientDetailsService clientDetailsService(DataSource dataSource) {
    
    
        ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        ((JdbcClientDetailsService) clientDetailsService).setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    //客户端详情服务
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    
    
        clients.withClientDetails(clientDetailsService);
    
    }


    //令牌管理服务
    @Bean
    public AuthorizationServerTokenServices tokenService() {
    
    
        DefaultTokenServices service=new DefaultTokenServices();
        service.setClientDetailsService(clientDetailsService);//客户端详情服务
        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;
    }

    //设置授权码模式的授权码如何存取,从数据库取
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
    
    
        return new JdbcAuthorizationCodeServices(dataSource);//设置授权码模式的授权码如何存取
    }

	//令牌访问端点
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    
    
        endpoints
                .authenticationManager(authenticationManager)//认证管理器
                .authorizationCodeServices(authorizationCodeServices)//授权码服务
                .tokenServices(tokenService())//令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

	//令牌访问端点安全策略
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security){
    
    
        security
                .tokenKeyAccess("permitAll()")                    //oauth/token_key是公开
                .checkTokenAccess("permitAll()")                  //oauth/check_token公开
                .allowFormAuthenticationForClients()				//表单认证(申请令牌)
        ;
    }

}

TokenConfig

@Configuration
public class TokenConfig {
    
    

    private String SIGNING_KEY = "uaa123";

    @Bean
    public TokenStore tokenStore() {
    
    
        //JWT令牌存储方案
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
    
    
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
        return converter;
    }

}

WebSecurityConfig

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

    //认证管理器
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
    
    
        return super.authenticationManagerBean();
    }
    //密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
    
    
        return new BCryptPasswordEncoder();
    }

    //安全拦截机制(最重要)
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/r/r1").hasAnyAuthority("p1")
                .antMatchers("/login*").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin();
    
    }
}

7.2 Centro de registro

Todas las solicitudes de microservicio pasan por la puerta de enlace, y la puerta de enlace lee la dirección del microservicio del centro de registro y reenvía la solicitud al microservicio.

1. Crear un proyecto maven
2. Pom.xml depende de lo siguiente

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>distributed-security</artifactId>
        <groupId>com.test.security</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>distributed-security-discovery</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

    </dependencies>

</project>

3. Archivo de configuración

spring:
  application:
    name: distributed-discovery

server:
  port: 53000 #启动端口

eureka:
  server:
    enable-self-preservation: false    #关闭服务器自我保护,客户端心跳检测15分钟内错误达到80%服务会保护,导致别人还认为是好用的服务
    eviction-interval-timer-in-ms: 10000 #清理间隔(单位毫秒,默认是60*1000)5秒将客户端剔除的服务在服务注册列表中剔除#
    shouldUseReadOnlyResponseCache: true #eureka是CAP理论种基于AP策略,为了保证强一致性关闭此切换CP 默认不关闭 false关闭
  client:
    register-with-eureka: false  #false:不作为一个客户端注册到注册中心
    fetch-registry: false      #为true时,可以启动,但报异常:Cannot execute request on any known server
    instance-info-replication-interval-seconds: 10
    serviceUrl:
      defaultZone: http://localhost:${
    
    server.port}/eureka/
  instance:
    hostname: ${
    
    spring.cloud.client.ip-address}
    prefer-ip-address: true
    instance-id: ${
    
    spring.application.name}:${
    
    spring.cloud.client.ip-address}:${
    
    spring.application.instance_id:${
    
    server.port}}

4. Clase de inicio:

@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServer {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DiscoveryServer.class,args);
    }
}

7.3 Puerta de enlace

La integración de la puerta de enlace OAuth2.0 tiene dos ideas, una es que el servidor de autenticación genera tokens jwt y todas las solicitudes se unifican en la capa de la puerta de enlace para verificación, evaluación de permisos y otras operaciones; la otra es procesada por varios servicios de recursos y la puerta de enlace solo reenvía solicitudes.

Elegimos el primero. Utilizamos la puerta de enlace API como función de servidor de recursos de OAuth2.0 para implementar la interceptación de permisos de clientes de acceso, el análisis de tokens y el reenvío de la información del usuario de inicio de sesión actual (jsonToken) a los microservicios, de modo que los microservicios posteriores no necesiten preocuparse por el análisis del formato del token y Mecanismo relacionado con OAuth2.0.

La puerta de enlace API es principalmente responsable de dos cosas en el sistema de autenticación y autorización:
(1) Como la función del servidor de recursos de OAuth2.0, realiza la intercepción de los permisos de las partes de acceso.
(2) El token analiza y reenvía la información del usuario conectado actualmente (token de texto claro) al microservicio

Después de que el microservicio obtiene el token de texto sin formato (el token de texto sin formato contiene la información de identidad y permiso del usuario que inició sesión), también debe hacer dos cosas: (1)
interceptación de la autorización del usuario (comprobar si el usuario actual tiene derecho a acceder). el recurso)
(2) Información del usuario Almacenada en el contexto del subproceso actual (beneficioso para la lógica comercial posterior para obtener información del usuario actual en cualquier momento)

7.3.1 Crear proyecto

1, pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>distributed-security</artifactId>
        <groupId>com.test.security</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>distributed-security-gateway</artifactId>
    <dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

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

        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

        <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.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
        </dependency>

        <dependency>
            <groupId>javax.interceptor</groupId>
            <artifactId>javax.interceptor-api</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

    </dependencies>

</project>

2. Archivo de configuración

spring.application.name=gateway-server
server.port=53010
spring.main.allow-bean-definition-overriding = true

logging.level.root = info
logging.level.org.springframework = info

zuul.retryable = true
zuul.ignoredServices = *
zuul.add-host-header = true
zuul.sensitiveHeaders = *

zuul.routes.uaa-service.stripPrefix = false
zuul.routes.uaa-service.path = /uaa/**

zuul.routes.order-service.stripPrefix = false
zuul.routes.order-service.path = /order/**

eureka.client.serviceUrl.defaultZone = http://localhost:53000/eureka/
eureka.instance.preferIpAddress = true
eureka.instance.instance-id = ${
    
    spring.application.name}:${
    
    spring.cloud.client.ip-address}:${
    
    spring.application.instance_id:${
    
    server.port}}
management.endpoints.web.exposure.include = refresh,health,info,env

feign.hystrix.enabled = true
feign.compression.request.enabled = true
feign.compression.request.mime-types[0] = text/xml
feign.compression.request.mime-types[1] = application/xml
feign.compression.request.mime-types[2] = application/json
feign.compression.request.min-request-size = 2048
feign.compression.response.enabled = true

El servicio de autenticación unificado (UAA) y el servicio de usuario unificado son microservicios bajo la puerta de enlace, y es necesario agregar nuevas configuraciones de enrutamiento en la puerta de enlace:

zuul.routes.uaa‐service.stripPrefix = false
zuul.routes.uaa‐service.path = /uaa/**
zuul.routes.user‐service.stripPrefix = false
zuul.routes.user‐service.path = /order/**

Si la URL de la solicitud recibida por la puerta de enlace configurada anteriormente coincide con la expresión /pedido/**, se reenviará al servicio de pedidos (servicio de usuario unificado).

Clase de inicio:

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class GatewayServer {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(GatewayServer.class, args);
    }
}

7.3.2, configuración de fichas

@Configuration
public class TokenConfig {
    
    

    private String SIGNING_KEY = "uaa123";

    @Bean
    public TokenStore tokenStore() {
    
    
        //JWT令牌存储方案
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
    
    
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
        return converter;
    }

}

7.3.3 Servicio de recursos de configuración

Defina la configuración del servicio de recursos en ResourceServerConfig. El contenido principal de la configuración es definir algunas reglas coincidentes para describir qué permisos necesita un cliente de acceso para acceder a un microservicio, como:

@Configuration
public class ResouceServerConfig  {
    
    

    public static final String RESOURCE_ID = "res1";


    //uaa资源服务配置
    @Configuration
    @EnableResourceServer
    public class UAAServerConfig extends ResourceServerConfigurerAdapter {
    
    
        @Autowired
        private TokenStore tokenStore;

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

        @Override
        public void configure(HttpSecurity http) throws Exception {
    
    
            http.authorizeRequests()
                 .antMatchers("/uaa/**").permitAll();
        }
    }


    //order资源
    //uaa资源服务配置
    @Configuration
    @EnableResourceServer
    public class OrderServerConfig extends ResourceServerConfigurerAdapter {
    
    
        @Autowired
        private TokenStore tokenStore;

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

        @Override
        public void configure(HttpSecurity http) throws Exception {
    
    
            http
                    .authorizeRequests()
                    .antMatchers("/order/**").access("#oauth2.hasScope('ROLE_API')");
        }
    }

}

Los recursos de dos microservicios se definen arriba, entre los cuales:
UAAServerConfig especifica que si la solicitud coincide con la puerta de enlace /uaa/**, no será interceptada.

OrderServerConfig especifica que si la solicitud coincide con /order/**, es decir, para acceder al servicio de usuario unificado, el cliente de acceso debe incluir lectura en el alcance y ROLE_USER en las autoridades (permisos).

Dado que res1 es un cliente de acceso, read incluye tres permisos: ROLE_ADMIN, ROLE_USER y ROLE_API.

7.3.4, configuración de seguridad

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
    
    
		http
			.authorizeRequests()
			.antMatchers("/**").permitAll()
			.and().csrf().disable();
	}
}

7.3 Reenviar el token de texto al microservicio

Se realiza mediante el filtro Zuul, el propósito es permitir que los microservicios posteriores obtengan fácilmente la información del usuario de inicio de sesión actual (token de texto claro)

(1) Implemente el prefiltro de Zuul, complete la extracción de la información del usuario de inicio de sesión actual y colóquela en la solicitud que reenvía el microservicio.

public class AuthFilter extends ZuulFilter {
    
    

    @Override
    public boolean shouldFilter() {
    
    
        return true;
    }

    @Override
    public String filterType() {
    
    
        return "pre";
    }

    @Override
    public int filterOrder() {
    
    
        return 0;
    }

    @Override
    public Object run() throws ZuulException {
    
    
        RequestContext ctx = RequestContext.getCurrentContext();
        //从安全上下文中拿 到用户身份对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(!(authentication instanceof OAuth2Authentication)){
    
    
            return null;
        }
        OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication;
        Authentication userAuthentication = oAuth2Authentication.getUserAuthentication();
        //取出用户身份信息
        String principal = userAuthentication.getName();

        //取出用户权限
        List<String> authorities = new ArrayList<>();
        //从userAuthentication取出权限,放在authorities
        userAuthentication.getAuthorities().stream().forEach(c->authorities.add(((GrantedAuthority) c).getAuthority()));

        OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request();
        Map<String, String> requestParameters = oAuth2Request.getRequestParameters();
        Map<String,Object> jsonToken = new HashMap<>(requestParameters);
        if(userAuthentication!=null){
    
    
            jsonToken.put("principal",principal);
            jsonToken.put("authorities",authorities);
        }

        //把身份信息和权限信息放在json中,加入http的header中,转发给微服务
        ctx.addZuulRequestHeader("json-token", EncryptUtil.encodeUTF8StringBase64(JSON.toJSONString(jsonToken)));

        return null;
    }
}

(2) Incorpore el filtro en el contenedor de primavera:
configure AuthFilter

@Configuration
public class ZuulConfig {
    
    

    @Bean
    public AuthFilter preFileter() {
    
    
        return new AuthFilter();
    }

    @Bean
    public FilterRegistrationBean corsFilter() {
    
    
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setMaxAge(18000L);
        source.registerCorsConfiguration("/**", config);
        CorsFilter corsFilter = new CorsFilter(source);
        FilterRegistrationBean bean = new FilterRegistrationBean(corsFilter);
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}

7.4 Intercepción de autenticación de usuarios de microservicios

Cuando el microservicio recibe un token de texto sin formato, ¿cómo se debe autenticar e interceptar? ¿Implementar un filtro usted mismo? ¿Analizar el token de texto sin formato por sí mismo y definir un conjunto de estrategias de acceso a recursos por sí mismo?

¿Se puede adaptar a Spring Security?¿De repente pensó en el ejemplo de autenticación basado en token de Spring Security que implementamos anteriormente? También tomamos el servicio de usuario unificado como el microservicio descendente de la puerta de enlace, lo modificamos y agregamos la función de interceptación de autenticación de usuario de microservicio.

(1) Agregar recursos de prueba
OrderController para agregar los siguientes puntos finales

@RestController
public class OrderController {
    
    

    @GetMapping(value = "/r1")
    @PreAuthorize("hasAuthority('p1')")//拥有p1权限方可访问此url
    public String r1(){
    
    
        //获取用户身份信息
        UserDTO  userDTO = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return userDTO.getFullname()+"访问资源1";
    }

	@PreAuthorize("hasAuthority('p2')")
	@GetMapping(value = "/r2")
	public String r2(){
    
    //通过Spring Security API获取当前登录用户
		UserDTO user = (UserDTO)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		return user.getUsername() + "访问资源2";
	}

}

(2) Configuración de Spring Security
Habilite la protección del método y agregue la estrategia de configuración de Spring Excepto el método /login que no está protegido (es necesario llamar a la autenticación unificada), todos los demás recursos necesitan autenticación para acceder.

@Override
public void configure(HttpSecurity http) throws Exception {
    
    
	http
		.authorizeRequests()
		.antMatchers("/**").access("#oauth2.hasScope('ROLE_ADMIN')")
		.and().csrf().disable()
		.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

Según la configuración anterior, hemos definido tres recursos en total. Aquellos con autorización p1 pueden acceder a los recursos r1, aquellos con autorización p2 pueden acceder a los recursos r2 y, siempre que pasen la autenticación, pueden acceder a los recursos r3.

(3) Defina el filtro para interceptar el token y formar el objeto Spring Security Authentication

@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {
    
    

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
    
    
            //解析出头中的token
        String token = httpServletRequest.getHeader("json-token");
        if(token!=null){
    
    
            String json = EncryptUtil.decodeUTF8StringBase64(token);
            //将token转成json对象
            JSONObject jsonObject = JSON.parseObject(json);
            //用户身份信息
//            UserDTO userDTO = new UserDTO();
//            String principal = jsonObject.getString("principal");
//            userDTO.setUsername(principal);
            UserDTO userDTO = JSON.parseObject(jsonObject.getString("principal"), UserDTO.class);
            //用户权限
            JSONArray authoritiesArray = jsonObject.getJSONArray("authorities");
            String[] authorities = authoritiesArray.toArray(new String[authoritiesArray.size()]);
            //将用户信息和权限填充 到用户身份token对象中
            UsernamePasswordAuthenticationToken authenticationToken
                    = new UsernamePasswordAuthenticationToken(userDTO,null, AuthorityUtils.createAuthorityList(authorities));
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
            //将authenticationToken填充到安全上下文
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);


        }
        filterChain.doFilter(httpServletRequest,httpServletResponse);

    }
}

A través del filtro anterior, el servicio de recursos puede obtener fácilmente la información de identidad del usuario:

UserDTO user = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

paso:

  • 1. Analizar el token
  • 2. Crear y completar autenticación
  • 3. Guarde la autenticación en el contexto de seguridad

Deje el resto a Spring Security.

Supongo que te gusta

Origin blog.csdn.net/shuai_h/article/details/130660731
Recomendado
Clasificación