SpringSecurtiy OAuth2 (3) Implicit Grant - 隐式模式

文章目录

  • 关于
    • 适用场景
    • 整体流程
  • 实现
    • 授权服务器 (Authorization Server)
      • AuthotizationServerConfiguration
      • SecurityConfiguration
    • 资源服务器 (Resource Server)
      • ResourceServerConfiguration
  • 测试
  • 总结

关于 SpringSecurity OAuth2 的 4 种模式的简要介绍性文章:
SpringSecurtiy OAuth2 (2) Authorization Code Grant - 授权码模式
SpringSecurtiy OAuth2 (3) Implicit Grant - 隐式授权模式
SpringSecurtiy OAuth2 (4) Resource Owner Password Grant - 密码模式
SpringSecurtiy OAuth2 (5) Client Credentials Grant - 客户端模式


更多细节和底层原理, 稍后会有专门的文章介绍.

关于

在 上一篇 中我们知道, 第三方应用获取授权码的 URL 形如:

http://localhost:18910/authorization-server/oauth/authorize?response_type=code&client_id=&client_secret=&scope=access

而本篇要介绍的隐式授权模式, 它的 URL 形如:

http://localhost:18777/authorization-server/oauth/authorize?response_type=token&client_id=&scope=access_resource

response_type=token, 表示直接返回 token. 整个过程没有后端介入, token 可以直接在前端, 所以很不安全. 隐式授权模式要求: 用户登录并对第三方应用进行授权, 直接返回访问 access-token, 通过token访问资源. 相比授权码模式, 它少了一次授权码的颁发与客户端使用授权码换取 access-token 的过程.
用户登陆后直接返回:

http://localhost:9001/callback#access_token=edc84920-5588-44f5-80d1-8d9cf79d3d14&token_type=bearer&expires_in=119

适用场景

  • 用户参与: 使用隐式授权需要与用户交互, 用户对授权服务器进行登录与授权
  • client_secret: 访问授权时, 不需要带第三方应用的 client_secret, 前提是资源服务校验 access_token 使用的客户端信息与第三方应用不同, 且配置了 client_secret.
  • 必须要有前端

整体流程

  1. 客户端让用户登陆授权服务器换 access_token
    1. 客户端 (浏览器或者 SPA) 将 client_id + 授权模式标识 (response_type) + 回调地址 (redirect_uri) 拼成 url 访问授权服务器的授权端点;
    2. 授权服务器跳转到用户登陆界面, 用户登陆
    3. 用户授权
    4. 授权服务器访问回调地址返回 access_token 给客户端的前端
  2. 客户端使用 access_token 访问资源
    1. 客户端在请求头中添加 access_token, 访问资源服务器;
    2. 资源服务器收到请求, 先调用校验 access_token 的方法 (可以是远程调用授权服务器的校验端点, 也可以直接访问授权存储器手动校对)
    3. 资源服务器校验成功, 返回资源

SpringSecurtiy OAuth2 (3) Implicit Grant - 隐式模式_第1张图片

实现

代码结构:

├─implicit-authorization-server
│  │  implicit-authorization-server.iml
│  │  pom.xml
│  │  README.md
│  │  
│  └─src
│     └─main
│         ├─java
│         │  └─c
│         │      └─c
│         │          └─d
│         │              └─s
│         │                  └─s
│         │                      └─o
│         │                          └─i
│         │                              └─authorization
│         │                                  └─server
│         │                                      │  ImplicitAuthorizationServer.java
│         │                                      │  
│         │                                      └─configuration
│         │                                              AuthorizationServerConfiguration.java
│         │                                              SecurityConfiguration.java
│         │                                              
│         └─resources
│                 application.yml
│
└─implicit-resource-server
   │  pom.xml
   │  
   └─src
      └─main
          ├─java
          │  └─c
          │      └─c
          │          └─d
          │              └─s
          │                  └─s
          │                      └─o
          │                          └─i
          │                              └─resource
          │                                  └─server
          │                                      │  ImplicitResourceServer.java
          │                                      │  
          │                                      ├─configuration
          │                                      │      ResourceServerConfiguration.java
          │                                      │      
          │                                      └─controller
          │                                              ResourceController.java
          │                                              
          └─resources
                  application.yml

授权服务器 (Authorization Server)

AuthotizationServerConfiguration

代码上和授权码模式差别不大, 需要关注的是在授权服务器中定义客户端的时候需要指定 authorizedGrantTypesimplicit.

/**
 * 授权服务器配置
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2020-06-10 11:43
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.allowFormAuthenticationForClients()
                // 参数与 security 访问控制一致
                .checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // @formatter:off
        clients.inMemory()

                // ~ client-a
                .withClient("client-a")
                    // .secret(passwordEncoder.encode("client-a-p"))
                    .accessTokenValiditySeconds(120)
                    .resourceIds("resource-server")
                    .scopes("access_resource")
                    .redirectUris("http://localhost:9001/callback")
                    .authorizedGrantTypes("implicit")

                .and()

                // ~ 资源服务器校验 token 时用的客户端信息, 仅需要 client_id 和 client_secret
                .withClient("resource-server")
                    .secret(passwordEncoder.encode("resource-server-p"))
        ;
        // @formatter:on
    }


    // ~ autowired
    // -----------------------------------------------------------------------------------------------------------------

    @Autowired
    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    // ~ bean
    // -----------------------------------------------------------------------------------------------------------------

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

SecurityConfiguration

/**
 * Spring Security 配置
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2020-06-10 11:43
 */
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private PasswordEncoder passwordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser(User.builder().username("caplike").password(passwordEncoder.encode("caplike-p")).authorities("USER").build());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin();
        http.authorizeRequests().anyRequest().authenticated();
    }

    // ~ autowired
    // -----------------------------------------------------------------------------------------------------------------

    @Autowired
    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }
}

资源服务器 (Resource Server)

ResourceServerConfiguration

/**
 * 资源服务器配置
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2020-06-11 15:14
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId("resource-server").tokenServices(remoteTokenServices()).stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated();
    }

    // -----------------------------------------------------------------------------------------------------------------

    /**
     * @return {@link RemoteTokenServices}
     */
    private RemoteTokenServices remoteTokenServices() {
        final RemoteTokenServices tokenServices = new RemoteTokenServices();
        tokenServices.setCheckTokenEndpointUrl("http://localhost:18777/implicit-authorization-server/oauth/check_token");
        tokenServices.setClientId("resource-server");
        tokenServices.setClientSecret("resource-server-p");
        return tokenServices;
    }

}

测试

分别启动两个服务器:
SpringSecurtiy OAuth2 (3) Implicit Grant - 隐式模式_第2张图片

总结

本篇简要介绍了 Spring Security OAuth2 Implicit Grant 的实现, 更多细节会在后续文章中详细阐述.
可以看到, 在用户登陆成功并授权后, 授权服务器直接返回了访问令牌. 而不是想授权码模式那样, 先返回一个授权码再有第三方应用使用授权码 POST 请求授权服务器换取令牌. 所以, 隐式授权模式的安全性低于授权码模式.

你可能感兴趣的:(#,Spring,Security)