OAuth2.0详细介绍与实践(通俗易懂)

Posted cv展示

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OAuth2.0详细介绍与实践(通俗易懂)相关的知识,希望对你有一定的参考价值。

一、OAuth2.0介绍

1.1 概述

  • OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是OAUTH的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAUTH是安全的。oAuth是Open Authorization的简写。
  • OAuth(开放授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不 需要将用户名和密码提供给第三方应用或分享他们数据的所有内容。OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth1.0即完全废止了OAuth1.0。
  • OAUTH是一种开放的协议,为桌面、手机或web应用提供了一种简单的,标准的方式去访问需要用户授权的API服务

1.2 特点

  • 简单:不管是OAUTH服务提供者还是应用开发者,都很易于理解与使用;
  • 安全:没有涉及到用户密钥等信息,更安全更灵活;
  • 开放:任何服务提供商都可以实现OAUTH,任何软件开发商都可以使用OAUTH;

1.2 Oauth2认证的例子

本例子是某网站使用微信认证的过程

  1. 用户借助微信认证登录某网站,用户就不用单独在某网站注册用户,怎么样算认证成功吗?
  2. 某网站需要成功从微信获取用户的身份信息则认为用户认证成功,那如何从微信获取用户的身份信息?
  3. 用户信息的拥有者是用户本人,微信需要经过用户的同意方可为某网站生成令牌,某网站拿此令牌方可从微信获取用户的信息。

第一步客户端(浏览器)请求第三方授权

用户进入网站的登录页面,点击微信的图标以微信账号登录系统,用户是自己在微信里信息的资源拥有者。点击“微信”出现一个二维码,此时用户扫描二维码,开始给客户端授权。

第二步:资源拥有者同意给客户端授权

资源拥有者扫描二维码表示资源拥有者同意给客户端授权,微信会对资源拥有者的身份进行验证, 验证通过后,微信会询问用户是否给授权客户端访问自己的微信数据,用户点击“确认登录”表示同意授权,微信认证服务器会颁发一个授权码,并重定向到客户端的网站。

第三步:客户端获取到授权码,请求认证服务器申请令牌

此过程用户看不到,客户端应用程序请求认证服务器,请求携带授权码。

第四步:认证服务器向客户端响应令牌

微信认证服务器验证了客户端请求的授权码,如果合法则给客户端颁发令牌,令牌是客户端访问资源的通行证。 此交互过程用户看不到,当客户端拿到令牌后,用户在网站上看到已经登录成功。

第五步:客户端请求资源服务器的资源

客户端携带令牌访问资源服务器的资源。

客户端网站携带令牌请求访问微信服务器获取用户的基本信息。

第六步:资源服务器返回受保护资源

资源服务器校验令牌的合法性,如果合法则向用户响应资源信息内容。

以上认证授权详细的执行流程如下:

1.4 官网上OAuth2.0认证过程

OAauth2.0包括以下角色:

  • 客户端

    本身不存储资源,需要通过资源拥有者的授权去请求资源服务器的资源,比如:android客户端、Web客户端(浏览器端)、微信客户端等。

  • 资源拥有者

    通常为用户,也可以是应用程序,即该资源的拥有者。

  • 授权服务器(也称认证服务器)

    用于服务提供商对资源拥有的身份进行认证、对访问资源进行授权,认证成功后会给客户端发放令牌 (access_token),作为客户端访问资源服务器的凭据。本例为微信的认证服务器。

  • 资源服务器

    存储资源的服务器,本例子为微信存储的用户信息。

现在还有一个问题,服务提供商能允许随便一个客户端就接入到它的授权服务器吗?答案是否定的,服务提供商会给准入的接入方一个身份,用于接入时的凭据: client_id:客户端标识

client_secret:客户端秘钥

因此,准确来说,授权服务器对两种OAuth2.0中的两个角色进行认证授权,分别是资源拥有者、客户端

1.5 授权流程

三个URL

  • Request Token URL: 获取未授权的Request Token服务地址;

  • User Authorization URL: 获取用户授权的Request Token服务地址;

  • Access Token URL: 用授权的Request Token换取Access Token的服务地址;

OAUTH认证授权就三个步骤,三句话可以概括:

  1. 获取未授权的Request Token
  2. 获取用户授权的Request Token
  3. 用授权的Request Token换取Access Token

当应用拿到Access Token后,就可以有权访问用户授权的资源了。大家可能看出来了,这三个步骤不就是对应OAUTH的三个URL服务地址嘛。上面的三个步骤中,每个步骤分别请求一个URL,并且收到相关信息,并且拿到上步的相关信息去请求接下来的URL直到拿到Access Token。

具体每步执行信息如下:

  1. 使用者(第三方软件)向OAUTH服务提供商请求未授权的Request Token。向Request Token URL发起请求,请求需要带上的参数。
  2. OAUTH服务提供商同意使用者的请求,并向其颁发未经用户授权的oauth_token与对应的oauth_token_secret,并返回给使用者。
  3. 使用者向OAUTH服务提供商请求用户授权的Request Token。向User Authorization URL发起请求,请求带上上步拿到的未授权的token与其密钥。
  4. OAUTH服务提供商将引导用户授权。该过程可能会提示用户,你想将哪些受保护的资源授权给该应用。此步可能会返回授权的Request Token也可能不返回。如Yahoo OAUTH就不会返回任何信息给使用者。
  5. Request Token 授权后,使用者将向Access Token URL发起请求,将上步授权的Request Token换取成Access Token。这个比第一步多了一个参数就是Request Token。
  6. OAUTH服务提供商同意使用者的请求,并向其颁发Access Token与对应的密钥,并返回给使用者。
  7. 使用者以后就可以使用上步返回的Access Token访问用户授权的资源。

从上面的步骤可以看出,用户始终没有将其用户名与密码等信息提供给使用者,从而更安全。

二、OAuth2实践

  • Spring-Security-OAuth2是对OAuth2的一种实现,并且跟我们之前学习的Spring Security相辅相成,与Spring Cloud体系的集成也非常便利
  • OAuth2.0的服务提供方涵盖两个服务,即授权服务 (Authorization Server,也叫认证服务) 和资源服务 (Resource Server),使用 Spring Security OAuth2 的时候你可以选择把它们在同一个应用程序中实现,也可以选择建立使用 同一个授权服务的多个资源服务。

**授权服务 (Authorization Server)**应包含对接入端以及登入用户的合法性进行验证并颁发token等功能,对令牌 的请求端点由 Spring MVC 控制器进行实现,下面是配置一个认证服务必须要实现的endpoints:

  • AuthorizationEndpoint 服务于认证请求。默认 URL: /oauth/authorize
  • TokenEndpoint 服务于访问令牌的请求。默认 URL:/oauth/token

资源服务 (Resource Server),应包含对资源的保护功能,对非法请求进行拦截,对请求中token进行解析鉴 权等,下面的过滤器用于实现 OAuth 2.0 资源服务:

  • OAuth2AuthenticationProcessingFilter用来对请求给出的身份令牌解析鉴权。

2.1 最简单的OAuth2进行分布式权限管理

项目一

认证授权demo流程图

1、客户端请求授权服务进行认证。

2、认证通过后由颁发令牌。

3、客户端携带令牌Token请求资源服务。

4、资源服务校验令牌的合法性,合法即返回资源信息。

demo说明图

2.1.1 授权服务搭建

  • 创建一个spring boot项目

  • 导入依赖

    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>cn.cvzhanshi</groupId>
        <artifactId>authorization_server</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>authorization_server</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-oauth2</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>$spring-cloud.version</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    
  • 配置文件

    # 应用名称
    spring:
      application:
        name: authorization_server
    server:
      port: 3001
    
  • security配置类

    /**
     * @author cVzhanshi
     * @create 2022-10-25 17:55
     */
    @Configuration
    @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter 
    
    
    
        @Bean
        public PasswordEncoder passwordEncoder()
            return new BCryptPasswordEncoder();
        
    
        //密码模式才需要配置,认证管理器
        @Bean
        @Override
        protected AuthenticationManager authenticationManager() throws Exception 
            return super.authenticationManager();
        
    
        @Override
        protected void configure(HttpSecurity http) throws Exception 
            http.csrf().disable()
                    .authorizeRequests()
                    .anyRequest().permitAll()
                    .and()
                    .formLogin()
                    .and()
                    .logout();
        
    
        // 模拟账号密码验证  就两个账号   user/user  admin/admin
        @Bean
        public UserDetailsService userDetailsService() 
            UserDetailsService userDetailsService = new UserDetailsService() 
                @Override
                public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
                    if (username.equals("user"))
                        MyUserDetails user = new MyUserDetails();
                        user.setUsername("user");
                        user.setPassword(passwordEncoder().encode("user"));
                        user.setPerms("user");
                        return user;
                    
                    if (username.equals("admin"))
                        MyUserDetails admin = new MyUserDetails();
                        admin.setUsername("admin");
                        admin.setPassword(passwordEncoder().encode("admin"));
                        admin.setPerms("admin");
                        return admin;
                    
                    return null;
                
            ;
            return userDetailsService;
        
    
    
  • 配置OAuth2.0 授权服务配置类

    • 可以用 @EnableAuthorizationServer 注解并继承AuthorizationServerConfigurerAdapter来配置OAuth2.0 授权服务器。

    • AuthorizationServerConfigurerAdapter要求配置以下几个类,这几个类是由Spring创建的独立的配置对象,它们 会被Spring传入AuthorizationServerConfigurer中进行配置。

      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:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在 这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
      • AuthorizationServerEndpointsConfigurer:用来配置令牌(token)的访问端点和令牌服务(token services)。
      • AuthorizationServerSecurityConfigurer:用来配置令牌端点的安全约束
    • 配置客户端详细信息

      • ClientDetailsServiceConfigurer 能够使用内存或者JDBC来实现客户端详情服务(ClientDetailsService), ClientDetailsService负责查找ClientDetails,而ClientDetails有几个重要的属性如下列表:

        • clientId:(必须的)用来标识客户的Id。
        • secret:(需要值得信任的客户端)客户端安全码(密码),如果有的话
        • scope:用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围。
        • authorizedGrantTypes:此客户端可以使用的授权类型,默认为空。
        • authorities:此客户端可以使用的权限(基于Spring Security authorities)。
        // 配置客户端详细信息,可以配置多个
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
            // 存储在缓存中,后期可以选择存在数据库中
            clients.inMemory()
                // 设置客户端的id和密码
                .withClient(("client1"))
                .secret(passwordEncoder.encode("123456"))
                // 给client一个id,这个在client的配置里要用的, 能使用的资源id
                .resourceIds("resource1")
        
                //允许的申请token的方式
                //authorization_code授权码模式,这个是标准模式
                //implicit简单模式,这个主要是给无后台的纯前端项目用的
                //password密码模式,直接拿用户的账号密码授权,不安全
                //client_credentials客户端模式,用clientid和密码授权,和用户无关的授权方式
                //refresh_token使用有效的refresh_token去重新生成一个token,之前的会失效
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                // 授权的范围,每个resource会设置自己的范围.
                .scopes("scope1","scope2")
                .autoApprove(false)
                .redirectUris("http://www.baidu.com");
        
            /*配置更多客户端
                        .and()
        
                        .withClient("client2")
                        .secret(passwordEncoder.encode("123123"))
                        .resourceIds("resource1")
                        .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                        .scopes("all")
                        .autoApprove(false)
                        .redirectUris("http://www.qq.com");*/
        
        
        
        
    • 管理令牌

      • AuthorizationServerTokenServices 接口定义了一些操作使得你可以对令牌进行一些必要的管理,令牌可以被用来加载身份信息,里面包含了这个令牌的相关权限。除了持久化令牌是委托一个 TokenStore 接口来实现以外,这个类几乎帮你做了 所有的事情。并且 TokenStore 这个接口有一个默认的实现,它就是 InMemoryTokenStore ,如其命名,所有的令牌是被保存在了内存中。除了InMemoryTokenStore 还有一些其他版本JdbcTokenStore:这是一个基于JDBC的实现版本,令牌会被保存进关系型数据库。JwtTokenStore。

      • 定义TokenConfig

        /**
         * @author cVzhanshi
         * @create 2022-10-25 18:18
         */
        @Configuration
        public class TokenConfig 
        	// 令牌的存储策略
            @Bean
            public TokenStore tokenStore()
                // 在内存中生成一个普通的令牌
                return new InMemoryTokenStore();
            
        
        
      • 定义AuthorizationServerTokenServices 在AuthorizationServer中定义AuthorizationServerTokenServices

        @Autowired
        private TokenStore tokenStore;
        @Autowired
        private ClientDetailsService clientDetailsService;
        
        
        //配置token管理服务
        @Bean
        public AuthorizationServerTokenServices tokenServices() 
            DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
            // 客户端信息服务
            defaultTokenServices.setClientDetailsService(clientDetailsService);
            // 是否产生刷新令牌
            defaultTokenServices.setSupportRefreshToken(true);
        
            //配置token的存储策略
            defaultTokenServices.setTokenStore(tokenStore);
            // 令牌的有效期
            defaultTokenServices.setAccessTokenValiditySeconds(300);
            // 刷新令牌的时间
            defaultTokenServices.setRefreshTokenValiditySeconds(1500);
            return defaultTokenServices;
        
        
    • 令牌访问端点配置

      AuthorizationServerEndpointsConfigurer 这个对象的实例可以完成令牌服务以及令牌endpoint配置。

      • 配置授权类型(Grant Types)AuthorizationServerEndpointsConfigurer 通过设定以下属性决定支持的授权类型(Grant Types):

        • authenticationManager:认证管理器,当你选择了资源所有者密码(password)授权类型的时候,请设置 这个属性注入一个 AuthenticationManager 对象。
        • userDetailsService:如果你设置了这个属性的话,那说明你有一个自己的 UserDetailsService 接口的实现, 或者你可以把这个东西设置到全局域上面去,当你设置了这个之后,那么 “refresh_token” 即刷新令牌授权类型模式的流程中就会包含一个检查,用来确保这个账号是否仍然有效,假如说你禁用了这个账户的话就授权失败。
        • authorizationCodeServices:这个属性是用来设置授权码服务的(即 AuthorizationCodeServices 的实例对 象),主要用于 “authorization_code” 授权码类型模式。
        • implicitGrantService:这个属性用于设置隐式授权模式,用来管理隐式授权模式的状态。
        • tokenGranter:当你设置了这个东西(即 TokenGranter 接口实现),那么授权将会交由你来完全掌控,并且会忽略掉上面的这几个属性,这个属性一般是用作拓展用途的,即标准的四种授权模式已经满足不了你的 需求的时候,才会考虑使用这个。
      • 配置授权端点的URL(Endpoint URLs):AuthorizationServerEndpointsConfigurer 这个配置对象有一个叫做 pathMapping() 的方法用来配置端点URL链 接,它有两个参数: 第一个参数:String 类型的,这个端点URL的默认链接。 第二个参数:String 类型的,你要进行替代的URL链接。

        以上的参数都将以 "/" 字符为开始的字符串,框架的默认URL链接如下列表,可以作为这个 pathMapping() 方法的第一个参数:
        /oauth/authorize:授权端点。
        /oauth/token:令牌端点。
        /oauth/confirm_access:用户确认授权提交端点。
        /oauth/error:授权服务错误信息端点。
        /oauth/check_token:用于资源服务访问的令牌解析端点。
        /oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话。
        需要注意的是授权端点这个URL应该被Spring Security保护起来只供授权用户访问.
        
      //密码模式才需要配置,认证管理器
      @Autowired
      private AuthenticationManager authenticationManager;
      @Bean
      public AuthorizationCodeServices authorizationCodeServices()  //设置授权码模式的授权码如何存取,暂时采用内存方式
          return new InMemoryAuthorizationCodeServices();
      
      @Autowired
      private AuthorizationCodeServices authorizationCodeServices;
      
      // 把上面的各个组件组合在一起
      @Override
      public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
          endpoints.authenticationManager(authenticationManager)//认证管理器
              .authorizationCodeServices(authorizationCodeServices)//授权码管理
              .tokenServices(tokenServices())//token管理
              .allowedTokenEndpointRequestMethods(HttpMethod.POST);
      
      
    • 令牌端点的安全约束

      // 令牌端点的安全约束
      //配置哪些接口可以被访问
      @Override
      public void configure(AuthorizationServerSecurityConfigurer security) throws Exception 
          security.tokenKeyAccess("permitAll()")//   /oauth/token_key公开
              .checkTokenAccess("permitAll()")//   /oauth/check_token公开
              .allowFormAuthenticationForClients();//允许表单认证
      
      
    • 授权配置类的完整代码

      /**
       * @author cVzhanshi
       * @create 2022-10-25 18:05
       */
      @Configuration
      //开启oauth2,auth server模式
      @EnableAuthorizationServer
      public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter 
      
          @Autowired
          private PasswordEncoder passwordEncoder;
      
          // 配置客户端详细信息,可以配置多个
          @Override
          public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
              // 存储在缓存中,后期可以选择存在数据库中
              clients.inMemory()
                  // 设置客户端的id和密码
                  .withClient(("client1"))
                  .secret(passwordEncoder.encode("123456"))
                  // 给client一个id,这个在client的配置里要用的, 能使用的资源id
                  .resourceIds("resource1")
      
                  //允许的申请token的方式
                  //authorization_code授权码模式,这个是标准模式
                  //implicit简单模式,这个主要是给无后台的纯前端项目用的
                  //password密码模式,直接拿用户的账号密码授权,不安全
                  //client_credentials客户端模式,用clientid和密码授权,和用户无关的授权方式
                  //refresh_token使用有效的refresh_token去重新生成一个token,之前的会失效
                  .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
       

      OAuth 2 实现单点登录,通俗易懂!

      点击关注公众号,Java干货及时送达

      作者:王克锋
      出处:https://kefeng.wang/2018/04/06/oauth2-sso/

      单点登录是多域名企业站点流行的登录方式。本文以现实生活场景辅助理解,力争彻底理清 OAuth2.0 实现单点登录的原理流程。同时总结了权限控制的实现方案,及其在微服务架构中的应用。

      1 什么是单点登录

      1.1 多点登录

      传统的多点登录系统中,每个站点都实现了本站专用的帐号数据库和登录模块。各站点的登录状态相互不认可,各站点需要逐一手工登录。如下图,有两个术语含义如下:

      1.2 单点登录

      单点登录,英文是 Single Sign On,缩写为 SSO。多个站点(192.168.1.20X)共用一台认证授权服务器(192.168.1.110,用户数据库和认证授权模块共用)。用户经由其中任何一个站点(比如 192.168.1.201)登录后,可以免登录访问其他所有站点。而且,各站点间可以通过该登录状态直接交互。

      2 OAuth2 认证授权的原理流程

      2.1 生活实例【★★重点★★】

      为了直观的理解 OAuth2.0 原理流程,我们假设这样一个生活场景:

      (1)档案局A(客户端 / Client):以“档案局ID/密码”标识,是掌握档案资源的机构。并列还有很多档案局B/C/…,每个档案局存储的档案内容(资源 / Resource)不一样,比如政治、经济、军事、文化等;

      (2)公民张三(资源所有者 / Resource Owner):以“用户名/密码”标识,需要到各个档案局查档案;

      (3)派出所(授权服务器 / Authentication Server):可以是单个巨大的派出所,也可以是数据共享的派出所集群,掌管的信息、提供的对外接口功能有:

      • 档案局信息:所有档案局的“档案局ID/密码”,证明档案局的身份;

      • 公民信息:所有公民的“用户名/密码”,能提供张三是张三的用户身份证明(认证 / Authentication)

      • 公民对于档案局的权限:有张公民和档案局的权限的映射表,可查得各公民对各档案局是否有操作权限(授权 / Authorization)。通常,设计中会增加官职(角色 / Role)一层,各公民属于哪个官职(角色),哪个官职(角色)对于特定档案局有操作权限。

      2.1.1 张三首次访问档案局A

      张三之前从未到访档案局,第一次来档案局。对照下图序号理解:

      (1)张三来到“档案局A”的“档案处”,该处要求实名登记后才能查询,被指示到“用户登记处”办理(HTTP重定向);

      (2)张三来到“档案局A”的“用户登记处”,既不能证明身份(认证),又不能证明自己有查档案A的权限(授权)。张三携带档案局A的标识(client-id),被重定向至“授权信开具处”;

      (3)张三来到“派出所”的“授权信开具处”,出示档案局A的标识,希望开具授权信(授权)。该处要求首先证明身份(认证),被重定向至“用户身份验证处”;

      (4)张三来到“派出所”的“用户身份验证处”,领取了用户身份表(网页登录表单 Form);

      (5)张三填上自己的用户名和密码,交给(提交 / Submit)“用户身份验证处”,该处从私用数据库中查得用户名密码匹配,确定此人是张三,开具身份证明信,完成 认证。张三带上身份证明信和档案局A的标识,被重定向至“授权信开具处”;

      (6)张三再次来到“授权信开具处”,出示身份证明信和档案局A的标识,该处从私用数据库中查得,张三的官职是市长级别(角色),该官职具有档案局A的查询权限,就开具“允许张三查询档案局A”的授权信(授权码 / code),张三带上授权信被重定向至“档案局”的“用户登录处”;

      (7)张三到了“档案局”的“用户登录处”,该处私下拿出档案局A的标识(client-id)和密码,再附上张三出示的授权信(code),向“派出所”的“腰牌发放处”为张三申请的“腰牌”(token),将来张三可以带着这个腰牌表明身份和权限。又被重定向到“档案处”;

      (8)张三的会话(Session)已经关联上了腰牌(token),可以直接通过“档案处”查档案。最新面试题整理好了,大家可以在Java面试库小程序在线刷题。

      2.1.2 张三首次访问档案局B

      张三已经成功访问了档案局A,现在他要访问档案局B。对照下图序号理解:最新面试题整理好了,大家可以在Java面试库小程序在线刷题。

      (1)/(2) 同上;

      (3)张三已经有“身份证明信”,直接在“派出所”的“授权信开具处”成功开具“访问档案局B”的授权信;

      (4)/(5)/(6) 免了;

      (7)“档案局B”的“用户登记处”完成登记;

      (8)“档案局B”的“档案处”查得档案。

      2.1.3 张三再次访问档案局A

      张三已经成功访问了档案局A,现在他要访问档案局A。对照下图序号理解:

      (1)直接成功查到了档案;

      (2~8)都免了。

      2.2 HTTP 重定向原理

      HTTP 协议中,浏览器的 REQUEST 发给服务器之后,服务器如果发现该业务不属于自己管辖,会把你支派到自身服务器或其他服务器(host)的某个接口(uri)。正如我们去政府部门办事,每到一个窗口,工作人员会说“你带上材料A,到本所的X窗口,或者其他Y所的Z窗口”进行下一个手续。

      2.3 SSO 工作流程

      至此,就不难理解 OAuth 2.0 的认证/授权流程,此处不再赘述。请拿下图对照“2.1 生活实例”一节来理解。

      2.4 OAuth2.0 进阶

      • https://tools.ietf.org/html/rfc6749

      • https://tools.ietf.org/html/rfc6750

      • https://blog.csdn.net/seccloud/article/details/8192707

      点击关注公众号,Java干货及时送达

      根据官方标准,OAuth 2.0 共用四种授权模式:

      • Authorization Code: 用在服务端应用之间,这种最复杂,也是本文采用的模式;

      • Implicit: 用在移动app或者web app(这些app是在用户的设备上的,如在手机上调起微信来进行认证授权)

      • Resource Owner Password Credentials(password): 应用直接都是受信任的(都是由一家公司开发的,本例子使用)

      • Client Credentials: 用在应用API访问。

      3 基于 SpringBoot 实现认证/授权

      3.1 授权服务器(Authorization Server)

      推荐一个 Spring Boot 基础教程及实战示例:

      https://github.com/javastacks/spring-boot-best-practice

      (1) pom.xml

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

      (2) application.properties

      server.port=8110 ## 监听端口

      (3) AuthorizationServerApplication.java

      @EnableResourceServer // 启用资源服务器
      public class AuthorizationServerApplication 
          // ...
      

      (4) 配置授权服务的参数

      @Configuration
      @EnableAuthorizationServer
      public class Oauth2AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter 
          @Override
          public void configure(final ClientDetailsServiceConfigurer clients) throws Exception 
              clients.inMemory()
                      .withClient("webapp").secret("secret") //客户端 id/secret
                      .authorizedGrantTypes("authorization code") //授权妈模式
                      .scopes("user_info")
                      .autoApprove(true) //自动审批
                      .accessTokenValiditySeconds(3600); //有效期1hour
          
      
      
      @Configuration
      public class Oauth2WebSecurityConfigurer extends WebSecurityConfigurerAdapter 
          @Override
          protected void configure(HttpSecurity http) throws Exception 
              http.requestMatchers()
                      .antMatchers("/login", "/oauth/authorize/oauth/logout")
                      .and().authorizeRequests().anyRequest().authenticated()
                      .and().formLogin().permitAll();
          
      
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception 
              auth.inMemoryAuthentication().withUser("admin").password("admin123").roles("ADMIN");
          
      

      3.2 客户端(Client, 业务网站)

      (1) pom.xml

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

      (2) application.properties

      server port=8080
      security.oauth2.client.client-id=webapp
      security.oauth2.client.client-secret=secret
      security.oauth2.client.access-token-uri=http://localhost:8110/oauth/token
      security.oauth2.client.user-authorization-uri=http://localhost:8110/oauth/authorize
      security.oauth2.resource.user-info-uri=http://localhost:8110/oauth/user

      (3) 配置 WEB 安全

      @Configuration
      @EnableOAuth2Sso
      public class Oauth2WebsecurityConfigurer extends WebSecurityConfigurerAdapter 
          @Override
          public void configure(HttpSecurity http) throws Exception 
              http.antMatcher("/**").authorizeRequests()
                      .antMatchers("/", "/login").permitAll()
                      .anyRequest().authenticated();
          
      
      
      @RestController
      public class Oauth2ClientController 
          @GetMapping("/")
          public ModelAndView index() 
              return new ModelAndView("index");
          
      
          @GetMapping("/welcome")
          public ModelAndView welcome() 
              return new ModelAndView("welcome");
          
      

      3.3 用户权限控制(基于角色)

      • 授权服务器中,定义各用户拥有的角色: user=USER, admin=ADMIN/USER, root=ROOT/ADMIN/USER

      • 业务网站中(client),注解标明哪些角色可

      @RestController
      public class Oauth2ClientController 
          @GetMapping("/welcome")
          public ModelAndView welcome() 
              return new ModelAndView("welcome");
          
      
          @GetMapping("/api/user")
          @PreAuthorize("hasAuthority('USER')")
          public Map<String, Object> apiUser() 
          
      
          @GetMapping("/api/admin")
          @PreAuthorize("hasAuthority('ADMIN')")
          public Map<String, Object> apiAdmin() 
          
      
          @GetMapping("/api/root")
          @PreAuthorize("hasAuthority('ROOT')")
          public Map<String, Object> apiRoot() 
          
      

      4 综合运用

      4.1 权限控制方案

      下图是基本的认证/授权控制方案,主要设计了认证授权服务器上相关数据表的基本定义。可对照本文“2.1 生活实例”一节来理解。

      4.2 在微服务架构中的应用

      与常规服务架构不同,在微服务架构中,Authorization Server/Resource Server 是作为微服务存在的,用户的登录可以通过API网关一次性完成,无需与无法跳转至内网的 Authorization Server 来完成。

      微信官宣:一大波新年红包封面来了!

      23 种设计模式实战(很全)

      Log4j 2.3.1 发布!又是什么鬼??

      Logback 也爆雷了,惊爆了。。。

      劲爆!Java 协程要来了。。。

      面试官:Java 8 map 和 flatMap 的区别?

      重磅官宣:Redis 对象映射框架来了!!

      推荐一款代码神器,代码量至少省一半!

      程序员精通各种技术体系,45岁求职难!

      重磅!Spring Boot 2.6 正式发布

      Spring Boot 学习笔记,这个太全了!

      关注Java技术栈看更多干货

      获取 Spring Boot 实战笔记!

      以上是关于OAuth2.0详细介绍与实践(通俗易懂)的主要内容,如果未能解决你的问题,请参考以下文章

      OAuth 2 实现单点登录,通俗易懂!

      麻烦用比较通俗易懂的语言帮我介绍一下JMS,中间件,webService,WSDL以及SOAP之间的关系.

      java基础面试+深度讲解—泛型 泛型讲解中最通俗易懂,最详细的一个版本

      通俗易懂地介绍跨语言微服务框架 Istio

      通俗易懂的机器学习——维度的诅咒(深入浅出表述机器学习降维的数学概念与实践)

      OAuth 2 实现单点登录,通俗易懂...