Después de escribir este caso, me temo que no puedo entender el proceso de inicio de sesión de OAuth2 con el entrevistador.

Ayer, presenté los conceptos básicos de OAuth2 con mis amigos. Antes de explicar Spring Cloud Security OAuth2, primero revisaré el código real para pasar por los distintos modos de autorización en OAuth2 con mis amigos. Hoy nos fijamos en los más utilizados y más Modelo de código de autorización complejo.

En este artículo, pasaré por una demostración completa . Tenga en cuenta que es una demostración completa , que lleva a mis amigos a recorrer el modo de código de autorización.

Si no ha leído el artículo anterior, puede leerlo primero. Esto lo ayudará a comprender algunos de los conceptos de este artículo:

1. Arquitectura del caso

Debido a que OAuth2 implica muchas cosas, la mayoría de los casos en línea están simplificados. Para muchos principiantes, los casos simplificados miran a las personas en la niebla, por lo que Songge quiere construir un caso de prueba completo esta vez. En este caso, incluye principalmente los siguientes servicios:

  • Aplicaciones de terceros
  • Servidor de autorizaciones
  • Servidor de recursos
  • Los usuarios

Yo uso una mesa para organizar para todos:

Proyecto Puerto Observaciones
servidor de autenticación 8080 Servidor de autorizaciones
servidor de usuario 8081 Servidor de recursos
aplicación-cliente 8082 Aplicaciones de terceros

Es decir, proporcionaré y probaré todos los roles involucrados en nuestro inicio de sesión en modo de código de autorización OAuth2 común, para maximizar la comprensión del principio de funcionamiento de OAuth2 (el código fuente del caso se puede descargar al final del artículo).

Nota: Los amigos primero deben mirar el proceso de inicio de sesión en modo de código de autorización OAuth2 descrito por Songge en el artículo anterior , y luego venir a estudiar este artículo.

Primero creemos un proyecto padre Maven vacío. Después de crearlo, no hay nada que agregar ni código para escribir. Construiremos este submódulo en este proyecto padre.

2. Configuración del servidor de autorización

Primero, creamos un servicio de autorización llamado auth-server. Al compilar, elegimos las siguientes tres dependencias:

  • web
  • seguridad en la nube de primavera
  • Nube espirituosa OAuth2

Después de crear el proyecto, primero proporcione una configuración básica de Spring Security:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("sang")
                .password(new BCryptPasswordEncoder().encode("123"))
                .roles("admin")
                .and()
                .withUser("javaboy")
                .password(new BCryptPasswordEncoder().encode("123"))
                .roles("user");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().formLogin();
    }
}

En este código, por razones de brevedad, no almacenaré al usuario de Spring Security en la base de datos, sino directamente en la memoria.

Aquí creé un usuario llamado sang con contraseña 123 y rol de administrador. También configuré un formulario de inicio de sesión.

El propósito de esta configuración es configurar usuarios. Por ejemplo, si desea utilizar WeChat para iniciar sesión en un sitio web de terceros, en este proceso, primero debe iniciar sesión en WeChat, que requiere su información de nombre de usuario / contraseña. Luego, lo que configuramos aquí es en realidad el nombre de usuario / contraseña / rol del usuario Información.

Una vez completada la configuración básica de la información del usuario, configuremos el servidor de autorización:

@Configuration
public class AccessTokenConfig {
    @Bean
    TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }
}
@EnableAuthorizationServer
@Configuration
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    @Autowired
    TokenStore tokenStore;
    @Autowired
    ClientDetailsService clientDetailsService;

    @Bean
    AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        services.setClientDetailsService(clientDetailsService);
        services.setSupportRefreshToken(true);
        services.setTokenStore(tokenStore);
        services.setAccessTokenValiditySeconds(60 * 60 * 2);
        services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
        return services;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("javaboy")
                .secret(new BCryptPasswordEncoder().encode("123"))
                .resourceIds("res1")
                .authorizedGrantTypes("authorization_code","refresh_token")
                .scopes("all")
                .redirectUris("http://localhost:8082/index.html");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authorizationCodeServices(authorizationCodeServices())
                .tokenServices(tokenServices());
    }
    @Bean
    AuthorizationCodeServices authorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }
}

Este código es un poco largo, déjame explicarte uno por uno:

  1. En primer lugar, proporcionamos una instancia de TokenStore. Esto se refiere a dónde desea almacenar el token generado. Podemos almacenarlo en Redis, también se puede almacenar en la memoria, o se puede combinar con JWT, etc. Aquí, primero lo almacenaremos en la memoria , Así que proporcione una instancia de InMemoryTokenStore.
  2. A continuación, creamos la clase AuthorizationServer heredada de AuthorizationServerConfigurerAdapter para configurar aún más el servidor de autorización. La clase AuthorizationServer recuerda agregar la anotación @EnableAuthorizationServer, lo que significa que la configuración automática del servidor de autorización está activada.
  3. En la clase AuthorizationServer, reescribimos tres métodos de configuración.
  4. AuthorizationServerSecurityConfigurer se usa para configurar las restricciones de seguridad del punto final del token, es decir, quién puede acceder a este punto final y quién no. checkTokenAccess se refiere a un punto final para la verificación del token. Configuramos este punto final para que sea directamente accesible (más tarde, cuando el servidor de recursos recibe el token, necesita verificar la validez del token y accederá a este punto final).
  5. ClientDetailsServiceConfigurer se utiliza para configurar la información detallada del cliente. En el último artículo , Songge les dijo a todos que el servidor de autorización necesita ser probado en dos aspectos, uno es verificar al cliente, el otro es verificar al usuario Usuario, ya lo hemos configurado antes, aquí está el cliente de verificación de configuración. Podemos almacenar la información del cliente en la base de datos, que en realidad es relativamente fácil, similar a almacenar la información del usuario en la base de datos, pero aquí para simplificar el código, todavía almaceno la información del cliente en la memoria, aquí hemos configurado la ID del cliente, secreto, id de recurso, tipo de autorización, alcance de autorización y uri de redireccionamiento. En el último artículo , hablé sobre cuatro tipos de autorización : los cuatro tipos no incluyen el tipo refresh_token, pero en la operación real, refresh_token también se cuenta como uno.
  6. AuthorizationServerEndpointsConfigurer se utiliza aquí para configurar el punto final de acceso a token y el servicio de token. authorizationCodeServices se usa para configurar el almacenamiento de códigos de autorización, aquí estamos almacenados en la memoria, y tokenServices se usa para configurar el almacenamiento de tokens, es decir, la ubicación de almacenamiento de access_token, aquí también estamos almacenados primero en la memoria. Algunos amigos preguntarán, ¿cuál es la diferencia entre el código de autorización y el token? El código de autorización se utiliza para obtener el token, y no será válido una vez utilizado. El token se utiliza para obtener recursos. Si está confundido, se recomienda volver a leer el artículo anterior . Ven y habla con todos
  7. El bean tokenServices se usa principalmente para configurar cierta información básica del Token, como si el Token admite la actualización, la ubicación de almacenamiento del Token, el período de validez del Token y el período de validez de la actualización del Token, etc. El período de validez del Token es fácil de entender. Permítanme hablar sobre la actualización del período de validez del Token. Cuando el Token está a punto de caducar, necesitamos obtener un nuevo Token. Al obtener un nuevo Token, necesitamos tener una información de credencial. Esta información de credencial no es la antigua Token. Pero otro refresh_token, este refresh_token también tiene un período de validez.

Bueno, después de esto, incluso si nuestro servidor de autorización está configurado, iniciaremos el servidor de autorización.

3. Configuración del servidor de recursos

Luego construimos un servidor de recursos. En los ejemplos que ve en línea, la mayoría de los servidores de recursos se colocan junto con el servidor de autorización. Si el proyecto es relativamente pequeño, está bien hacerlo, pero si es un proyecto grande, este enfoque no es adecuado.

El servidor de recursos se usa para almacenar los recursos del usuario, como sus imágenes y openid en WeChat. Después de que el usuario obtiene el access_token del servidor de autorización, el servidor de recursos puede solicitar datos a través del access_token.

Creamos un nuevo proyecto Spring Boot, llamado user-server, como nuestro servidor de recursos, al crear, agregamos las siguientes dependencias:

Una vez que el proyecto se haya creado correctamente, agregue la siguiente configuración:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Bean
    RemoteTokenServices tokenServices() {
        RemoteTokenServices services = new RemoteTokenServices();
        services.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
        services.setClientId("javaboy");
        services.setClientSecret("123");
        return services;
    }
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("res1").tokenServices(tokenServices());
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .anyRequest().authenticated();
    }
}

Este código de configuración es muy simple, déjenme decir brevemente:

  1. tokenServices Hemos configurado una instancia de RemoteTokenServices. Esto se debe a que el servidor de recursos y el servidor de autorización están separados. El servidor de recursos y el servidor de autorización están juntos. No hay necesidad de configurar RemoteTokenServices.
  2. En RemoteTokenServices, configuramos la dirección de verificación access_token, client_id y client_secret. Cuando los usuarios acuden al servidor de recursos para solicitar recursos, llevarán un access_token. A través de la configuración aquí, podemos verificar si el token es correcto.
  3. Finalmente, configure las reglas de intercepción de recursos. Esta es la redacción básica en Spring Security, y no las repetiré.

A continuación, configuremos dos interfaces de prueba:

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
    @GetMapping("/admin/hello")
    public String admin() {
        return "admin";
    }
}

Después de esto, incluso si nuestro servidor de recursos está configurado correctamente.

4. Creación de aplicaciones de terceros.

A continuación, cree nuestra aplicación de terceros.

Tenga en cuenta que la aplicación de terceros no es necesaria, el código escrito a continuación también se puede probar con POSTMAN, este pequeño socio puede probarlo por sí mismo.

La aplicación de terceros es un proyecto Spring Boot ordinario, la dependencia de Thymeleaf y la dependencia web se agregan al crear:

En el directorio de recursos / plantillas, cree index.html con el siguiente contenido:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>江南一点雨</title>
</head>
<body>
你好,江南一点雨!

<a href="http://localhost:8080/oauth/authorize?client_id=javaboy&response_type=code&scope=all&redirect_uri=http://localhost:8082/index.html">第三方登录</a>

<h1 th:text="${msg}"></h1>
</body>
</html>

Esta es una plantilla Thymeleaf. Haga clic en el hipervínculo para lograr el inicio de sesión de terceros. Los parámetros del hipervínculo son los siguientes:

  • client_id ID de cliente, complete de acuerdo con nuestra configuración real en el servidor de autorización.
  • response_type indica el tipo de respuesta. Aquí, el código indica que la respuesta es un código de autorización.
  • redirect_uri representa la dirección de redireccionamiento después de una autorización exitosa, y aquí representa regresar a la página de inicio de la aplicación de terceros.
  • alcance indica el alcance de la autorización.

Los datos en la etiqueta h1 provienen del servidor de recursos. Cuando el servidor de autorización pasa, llevamos el token de acceso al servidor de recursos para cargar los datos. Los datos cargados se muestran en la etiqueta h1.

A continuación, definamos un HelloController:

@Controller
public class HelloController {
    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/index.html")
    public String hello(String code, Model model) {
        if (code != null) {
            MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
            map.add("code", code);
            map.add("client_id", "javaboy");
            map.add("client_secret", "123");
            map.add("redirect_uri", "http://localhost:8082/index.html");
            map.add("grant_type", "authorization_code");
            Map<String,String> resp = restTemplate.postForObject("http://localhost:8080/oauth/token", map, Map.class);
            String access_token = resp.get("access_token");
            System.out.println(access_token);
            HttpHeaders headers = new HttpHeaders();
            headers.add("Authorization", "Bearer " + access_token);
            HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
            ResponseEntity<String> entity = restTemplate.exchange("http://localhost:8081/admin/hello", HttpMethod.GET, httpEntity, String.class);
            model.addAttribute("msg", entity.getBody());
        }
        return "index";
    }
}

En este HelloController, definimos la dirección de /index.html.

Si el código no es nulo, es decir, si se redirige a esta dirección a través del servidor de autorización, entonces hacemos las dos operaciones siguientes:

  1. Según para obtener el código, a la solicitud http://localhost:8080/oauth/tokende direcciones para obtener el token, los datos devueltos se estructura de la siguiente manera:
{
    "access_token": "e7f223c4-7543-43c0-b5a6-5011743b5af4",
    "token_type": "bearer",
    "refresh_token": "aafc167b-a112-456e-bbd8-58cb56d915dd",
    "expires_in": 7199,
    "scope": "all"
}

access_token es el token que necesitamos para solicitar datos, refresh_token es el token que necesitamos para actualizar el token, expires_in indica cuánto tiempo es válido el token.

  1. A continuación, solicite el servidor de recursos en función del acceso_token que obtuvimos. Tenga en cuenta que el acceso_token se pasa a través del encabezado de la solicitud y finalmente coloque los datos devueltos por el servidor de recursos en el modelo.

Aquí solo doy un ejemplo simple, el propósito es pasar este proceso con todos. Normalmente, es posible que necesitemos una tarea programada para mantener el access_token, en lugar de obtenerlo cada vez que se solicita la página, y obtener periódicamente el último access_token. . En artículos posteriores, Song Ge continuará mejorando este caso y luego regresará para resolver estos detalles con usted.

OK, después de escribir el código, podemos iniciar la aplicación de terceros y comenzar las pruebas.

5. Prueba

A continuación vamos a probar.

Primero fuimos a visitar http://localhost:8082/index.htmlla página con los siguientes resultados:

Luego hacemos clic en el hipervínculo de inicio de sesión de terceros, y después de hacer clic, ingresaremos a la página de inicio de sesión predeterminada del servidor de autorización:

A continuación, ingresamos la información del usuario configurada en el servidor de autorización para iniciar sesión. Después de iniciar sesión correctamente, verá la siguiente página:

En esta página, podemos ver un mensaje que pregunta si autoriza al usuario javaboy a acceder al recurso protegido, elegimos aprobar y luego hacemos clic en el botón Autorizar a continuación. Después de hacer clic, la página saltará automáticamente a mí De aplicaciones de terceros:

Tenga en cuenta que en este momento, hay un parámetro de código en la barra de direcciones, que es el código de autorización proporcionado por el servidor de autorización. Con este código de autorización, podemos solicitar access_token, y el código de autorización será inválido una vez utilizado.

Al mismo tiempo, todos notan que hay un administrador adicional en la página. Este administrador son los datos solicitados del servidor de recursos.

Por supuesto, hemos configurado dos usuarios en el servidor de autorización, también puede intentar iniciar sesión con el usuario javaboy / 123, porque este usuario no tiene la función de administrador, por lo que este usuario no podrá obtener la cadena de administración, el mensaje de error es el siguiente :

Este pequeño amigo puede probarlo por mí mismo, no lo mostraré nuevamente.

Finalmente, estoy diciendo que esta no es la versión definitiva, sino un prototipo. En el siguiente artículo, Song Ge lo llevará a continuar mejorando este caso.

Bueno, siga la cuenta pública de WeChat Jiangnan un poco de lluvia , responda oauth2 Descargue el caso completo de este artículo.

发布了574 篇原创文章 · 获赞 6896 · 访问量 476万+

Supongo que te gusta

Origin blog.csdn.net/u012702547/article/details/105504300
Recomendado
Clasificación