vlambda博客
学习文章列表

读书笔记《spring-security-third-edition》开放 OAuth 2

开放 OAuth 2

OAuth 2 是一种非常流行的受信任身份管理形式,它允许用户通过一个受信任的提供商来管理他们的身份。这个方便的功能为用户提供了将密码和个人信息存储在受信任的 OAuth 2 提供商处的安全性,并可以根据要求选择性地披露个人信息。此外,启用 OAuth 2 的网站让您确信提供 OAuth 2 凭据的用户就是他们所说的那个人。

在本章中,我们将介绍以下主题:

  • Learning to set up your own OAuth 2 application in less than 5 minutes
  • Configuring the JBCP calendar application with a very rapid implementation of OAuth 2
  • Learning the conceptual architecture of OAuth 2 and how it provides your site with trustworthy user access
  • Implementing OAuth 2-based user registration
  • Experimenting with OAuth 2 attribute exchange for user profile functionality
  • Demonstrating how we can trigger automatic authentication to the previous OAuth 2 provider
  • Examining the security offered by OAuth 2-based login

OAuth 2 充满希望的世界

作为应用程序开发人员,您可能听说过很多关于 OAuth 2 的术语。 OAuth 2 已被世界各地的 Web 服务和软件公司广泛采用,并且是这些公司交互和共享信息方式不可或缺的一部分。但它到底是什么?简而言之,OAuth 2 是一种允许不同方以安全可靠的方式共享信息和资源的协议。

OAuth 1.0 怎么样?

出于同样的动机,OAuth 1.0 于 2007 年设计并获得批准。但是,它被批评为过于复杂,并且还存在规范不精确的问题,从而导致实施不安全。所有这些问题都导致 OAuth 1.0 的采用率很低,并最终导致了 OAuth 2 的设计和创建。OAuth 2 是 OAuth 1.0 的继任者。

还需要注意的是,OAuth 2 不向后兼容 OAuth 1.0,因此 OAuth 2 应用程序无法与 OAuth 1.0 服务提供商集成。

这种类型的登录(通过受信任的第三方)已经存在了很长时间,以多种不同的形式(例如,Microsoft Passport 成为网络上最著名的中央登录服务之一一段时间)。 OAuth 2 的显着优势是 OAuth 2 提供者只需实现公共 OAuth 2 协议即可与任何寻求将登录与 OAuth 2 集成的站点兼容。

您可以参考 OAuth 2.0 规范,网址为 https://tools.ietf.org/html/rfc6749

下图说明了在登录过程中集成 OAuth 2 的站点与 Facebook OAuth 2 提供者之间的高级关系,例如:

读书笔记《spring-security-third-edition》开放 OAuth 2

我们可以看到提交表单帖子将向 OAuth 提供者发起请求,导致提供者显示一个授权对话框,要求用户允许 jbcpcalendar 从您的 OAuth 提供者帐户获取特定信息的权限。此请求包含一个名为 codeuri 参数。一旦授予,用户将被重定向回 jbcpcalendar code 参数包含在 uri 参数中。然后,请求再次被重定向到 OAuth 提供者 以授权 jbcpcalendar。然后,OAuth 提供者以 access_token 进行响应,可用于访问 jbcpcalendar 被授予访问权限的用户的 OAuth 信息。

不要明确相信 OAuth 2!

在这里,您可以看到一个可以欺骗系统用户的基本假设。我们可以注册一个 OAuth 2 提供者帐户,这会使我们看起来好像是 James Gosling,尽管我们显然不是。不要做出错误的假设,即仅仅因为用户有一个令人信服的 OAuth 2(或 OAuth 2 委托提供者),他/她就是真实的人,而不需要额外的身份证明。换个角度想一想,如果有人上门声称他是詹姆斯·高斯林,你会在不验证身份的情况下让他进来吗?

然后,启用 OAuth 2 的应用程序将用户重定向到 OAuth 2 提供者,用户将其凭据提供给提供者,然后由提供者负责做出访问决定。一旦提供者做出访问决定,提供者将用户重定向到发起站点,该站点现在可以确保用户的真实性。一旦您尝试过 OAuth 2,就会更容易理解。现在让我们将 OAuth 2 添加到 JBCP 日历登录屏幕!

注册 OAuth 2 应用程序

为了从本节中的练习中获得全部价值(并能够测试登录),您需要使用服务提供者创建一个应用程序。目前,Spring Social 支持 Twitter、Facebook、Google、LinkedIn 和 GitHub,并且列表还在不断增加。

为了充分发挥本章练习的价值,我们建议您至少拥有 Twitter 和 GitHub 的帐户。我们已经为 jbcpcalendar 应用程序设置了帐户,我们将在本章的其余部分使用它。

使用 Spring Security 启用 OAuth 身份验证

其他必需的依赖项

让我们看一下以下步骤:

  1. In order to utilize OAuth, we will need to include provider-specific dependencies and their transitive dependencies. This can be done in Gradle by updating the build.gradle file, as shown in the following code snippet:
        //build.gradle

        compile("org.springframework.boot:spring-boot-starter-
        social-facebook")
        compile("org.springframework.boot:spring-boot-starter-
        social-linkedin")
        compile("org.springframework.boot:spring-boot-starter-
        social-twitter")
  1. Using Spring Boot includes references to Facebook, Twitter, and LinkedIn starter dependencies, as shown in the preceding code snippet. To add other providers, we must include the provider dependency and include the version. This can be done in Gradle by updating the build.gradle file, as shown in the following code snippet:
        //build.gradle

        compile("org.springframework.social:spring-social-google:
        latest.release ")
        compile("org.springframework.social:spring-social-github:
        latest.release ")
        compile("org.springframework.social:spring-social-linkedin:
        latest.release ")
你应该从源代码开始 chapter09.00-日历
  1. When writing the OAuth login form, we will need to replace the username and password fields with OAuth fields. Go ahead and make the following updates to your login.html file:
        //src/main/resources/templates/login.html

         <div class="form-actions">
            <input id="submit" class="btn" name="submit" type="submit" value="Login"/>
           </div>
         </form>
       <br/>
         <h3>Social Login</h3>
       <br />
        <form th:action="@{/signin/twitter}" method="POST" class="form-horizontal">
         <input type="hidden" name="scope" value="public_profile" />
        <div class="form-actions">
        <input id="twitter-submit" class="btn" type="submit" value="Login using Twitter"/>
         </div>
        </form>
       </div>
  1. We can make similar edits to the signup form, as shown in the following code snippet:
         //src/main/resources/templates/signup/form.html

        </fieldset>
        </form>
         <br/>
           <h3>Social Login</h3>
         <br/>
           <form th:action="@{/signin/twitter}" method="POST" class="form-horizontal">
           <input type="hidden" name="scope" value="public_profile" />
        <div class="form-actions">
         <input id="twitter-submit" class="btn" type="submit" value="Login using Twitter"/>
        </div>
        </form>
         </div>

您会注意到我们添加了一个范围字段来定义我们有兴趣在身份验证期间检索的 OAuth 2 详细信息。

OAuth 2.0 API 范围:范围允许提供者定义客户端应用程序可访问的 API 数据。当提供者创建 API 时,他们为每个表示的 API 和操作定义一个范围。创建 API 并定义范围后,客户端应用程序可以在启动授权流程时请求这些定义的权限,并将它们作为范围请求参数的一部分包含在访问令牌中。

每个提供者的 API 范围可能略有不同,例如 r_basicprofiler_emailaddress,但 API 范围也仅限于应用程序配置。因此,应用程序可能只请求访问电子邮件或联系人,而不是整个用户配置文件或提供者操作,例如发布到用户的墙上。

您会注意到我们不提供带有 OAuth 2 登录的记住我选项。这是因为与供应商之间的重定向导致 remember me 复选框值丢失,因此当用户成功通过身份验证时,他们不再拥有 remember me< /strong> 选项指示。这是不幸的,但最终增加了 OAuth 2 作为我们网站登录机制的安全性,因为 OAuth 2 强制用户在每次登录时都与提供商建立信任关系。

在 Spring Security 中配置 OAuth 2 支持

使用 Spring Social,我们可以启用 OAuth 2 特定的提供者端点来拦截提供者表单提交。

本地用户连接存储库

UsersConnectionRepository 接口是一个数据访问接口,用于管理用户与服务提供者的连接的全局存储。它提供了适用于多条用户记录的数据访问操作,如以下代码片段所示:

    //src/main/java/com/packtpub/springsecurity/configuration/SocialConfig.java

    @Autowired
    private UsersConnectionRepository usersConnectionRepository;
    @Autowired
     private ProviderConnectionSignup providerConnectionSignup;
    @Bean
    public ProviderSignInController providerSignInController() {
       ((JdbcUsersConnectionRepository) usersConnectionRepository)
       .setConnectionSignUp(providerConnectionSignup);
       ...
    }

为提供者详细信息创建本地数据库条目

Spring Security 支持将提供者详细信息保存在一组单独的数据库表中,以防我们希望将用户保存在本地数据存储中,但不想将该数据包含在现有 User 中桌子:

    //src/main/java/com/packtpub/springsecurity/configuration/
    SocialDatabasePopulator.java

    @Component
    public class SocialDatabasePopulator
    implements InitializingBean {
       private final DataSource dataSource;
       @Autowired
    public SocialDatabasePopulator(final DataSource dataSource) {
    this.dataSource = dataSource;
     }
    @Override
    public void afterPropertiesSet() throws Exception {
       ClassPathResource resource = new ClassPathResource(
       "org/springframework/social/connect/jdbc/
       JdbcUsersConnectionRepository.sql");
       executeSql(resource);
     }
    private void executeSql(final Resource resource) {
     ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
     populator.setContinueOnError(true);
     populator.addScript(resource);
     DatabasePopulatorUtils.execute(populator, dataSource);
     }
  }

这个 InitializingBean 接口在加载时执行,将执行 JdbcUsersConnectionRepository.sql,它位于 spring-social-core-[VERSION].jar 类路径上的文件,将以下模式播种到我们的本地数据库中:

    spring-social-core-  [VERSION].jar#org/springframework/social/connect/jdbc/
    JdbcUsersConnectionRepository.sql

    create table UserConnection(
      userId varchar(255) not null,
      providerId varchar(255) not null,
      providerUserId varchar(255),
      rank int not null,
      displayName varchar(255),
      profileUrl varchar(512),
      imageUrl varchar(512),
      accessToken varchar(512) not null,
      secret varchar(512),
      refreshToken varchar(512),
      expireTime bigint,
      primary key (userId, providerId, providerUserId));

      create unique index UserConnectionRank on UserConnection(userId, providerId,  
      rank);

现在我们有了一个存储提供程序详细信息的表,我们可以配置 ConnectionRepository 以在运行时保存提供程序详细信息。

自定义 UserConnectionRepository 接口

我们需要创建一个 UserConnectionRepository 接口,我们可以利用 JdbcUsersConnectionRepository 作为实现,它基于我们在生成的 JdbcUsersConnectionRepository.sql 模式加载时间,如下:

      //src/main/java/com/packtpub/springsecurity/configuration/
      DatabaseSocialConfigurer.java

      public class DatabaseSocialConfigurer extends SocialConfigurerAdapter {
        private final DataSource dataSource;
        public DatabaseSocialConfigurer(DataSource dataSource) {
         this.dataSource = dataSource;
       }
      @Override
      public UsersConnectionRepository getUsersConnectionRepository(
      ConnectionFactoryLocator connectionFactoryLocator) {
          TextEncryptor textEncryptor = Encryptors.noOpText();
          return new JdbcUsersConnectionRepository(
          dataSource, connectionFactoryLocator, textEncryptor);
     }
      @Override
     public void addConnectionFactories(ConnectionFactoryConfigurer config,
     Environment env) {
          super.addConnectionFactories(config, env);
       }
   }

现在,每次用户连接到注册的提供商时,连接详细信息都会保存到我们的本地数据库中。

ConnectionSignup 流程

为了将提供者详细信息保存到本地存储库中,我们创建了一个 ConnectionSignup 对象,这是一个在没有 userid 可以注册新用户的情况下的命令从 Connection 映射,这允许在提供者登录尝试期间从连接数据隐式创建本地用户配置文件:

    //src/main/java/com/packtpub/springsecurity/authentication/
    ProviderConnectionSignup.java

    @Service
     public class ProviderConnectionSignup implements ConnectionSignUp {
        ...; 
    @Override
    public String execute(Connection<?> connection) {
       ...
     }
    }

执行 OAuth 2 提供者连接工作流

为了保存提供者的详细信息,我们需要从提供者那里获取可用的详细信息,可以通过 OAuth 2 连接获得。接下来,我们根据可用的详细信息创建一个 CalendarUser 表。请注意,我们需要创建至少一个 GrantedAuthority 角色。在这里,我们使用 CalendarUserAuthorityUtils#createAuthorities 来创建 ROLE_USER GrantedAuthority

    //src/main/java/com/packtpub/springsecurity/authentication/
    ProviderConnectionSignup.java

    @Service
    public class ProviderConnectionSignup implements ConnectionSignUp {
         ...
    @Override
    public String execute(Connection<?> connection) {
        UserProfile profile = connection.fetchUserProfile();
        CalendarUser user = new CalendarUser();
        if(profile.getEmail() != null){
             user.setEmail(profile.getEmail());
          }
        else if(profile.getUsername() != null){
             user.setEmail(profile.getUsername());
         }
        else {
             user.setEmail(connection.getDisplayName());
         }
             user.setFirstName(profile.getFirstName());
             user.setLastName(profile.getLastName());
             user.setPassword(randomAlphabetic(32));
             CalendarUserAuthorityUtils.createAuthorities(user);
             ...
         }
      }


添加 OAuth 2 用户

现在我们已经从我们的提供者详细信息创建了 CalendarUser,我们需要使用 CalendarUserDao 将该 User 帐户保存到我们的数据库中。然后我们返回 CalendarUser 电子邮件,因为这是我们在 JBCP 日历中使用的用户名,如下所示:

//src/main/java/com/packtpub/springsecurity/authentication/
ProviderConnectionSignup.java

@Service
public class ProviderConnectionSignup
implements ConnectionSignUp {
 @Autowired
   private CalendarUserDao calendarUserDao;
  @Override
 public String execute(Connection<?> connection) {...
calendarUserDao.createUser(user);
return user.getEmail();
   }
}

现在,我们的数据库中有一个基于提供者详细信息的本地 User 帐户。

这是一个额外的数据库条目,因为我们已经将提供者详细信息保存到 UserConnection 表之前。

OAuth 2 控制器登录流程

现在,要完成SocialConfig.java的配置,我们需要构造ProviderSignInController,用ConnectionFactoryLocatorusersConnectionRepository 和 SignInAdapterProviderSignInController 接口是一个 Spring MVC 控制器,用于处理提供者用户登录流程。对 /signin/{providerId} 的 HTTP POST 请求启动用户使用 {providerId} 登录。向 /signin/{providerId}?oauth_token&amp;oauth_verifier||code 提交 HTTP GET 请求将收到 {providerId} 身份验证回调并建立连接。

ServiceLocator 接口用于创建 ConnectionFactory 实例。该工厂支持 providerIdapiType 查找,基于 Spring Boot 的 AutoConfiguration 中包含的服务提供者:

    //src/main/java/com/packtpub/springsecurity/configuration/SocialConfig.java

    @Autowired
    private ConnectionFactoryLocator connectionFactoryLocator;
    @Bean
    public ProviderSignInController providerSignInController() {
        ...
        return new ProviderSignInController(connectionFactoryLocator,
        usersConnectionRepository, authSignInAdapter());
    }

这将允许截获到特定提供者 uri 的提交,并开始 OAuth 2 连接流程。

自动用户认证

让我们看一下以下步骤:

  1. The ProviderSignInController controller is initialized with an authentication SignInAdapter, which is used to complete a provider sign-in attempt by signing in the local user account with the specified ID:
        //src/main/java/com/packtpub/springsecurity/configuration/
        SocialConfig.java

        @Bean
        public SignInAdapter authSignInAdapter() {
           return (userId, connection, request) -> {
             SocialAuthenticationUtils.authenticate(connection);
             return null;
           };
         }
  1. In the SingInAdapter bean, from the preceding code snippet, we used a custom authentication utility method to create an Authentication object in the form of UsernamePasswordAuthenticationToken, and added it to SecurityContext based on the details returned from the OAuth 2 provider:
        //src/main/java/com/packtpub/springsecurity/authentication/
        SocialAuthenticationUtils.java

        public class SocialAuthenticationUtils {
       public static void authenticate(Connection<?> connection) {
         UserProfile profile = connection.fetchUserProfile();
         CalendarUser user = new CalendarUser();
         if(profile.getEmail() != null){
             user.setEmail(profile.getEmail());
           }
         else if(profile.getUsername() != null){
             user.setEmail(profile.getUsername());
          }
         else {
             user.setEmail(connection.getDisplayName());
           }
             user.setFirstName(profile.getFirstName());
             user.setLastName(profile.getLastName());
             UsernamePasswordAuthenticationToken authentication = new  
             UsernamePasswordAuthenticationToken(user, null,        
             CalendarUserAuthorityUtils.createAuthorities(user));
             SecurityContextHolder.getContext()
             .setAuthentication(authentication);
           }
        }

连接到提供程序所需的最终详细信息是创建提供程序应用程序时获得的应用程序 ID 和密钥如下:

        //src/main/resources/application.yml:

        spring
        ## Social Configuration:
        social:
        twitter:
          appId: cgceheRX6a8EAE74JUeiRi8jZ
          appSecret: XR0J2N0Inzy2y2poxzot9oSAaE6MIOs4QHSWzT8dyeZaaeawep
  1. Now we have the required details to connect to the Twitter JBCP calendar, and we can start the JBCP calendar and log in with a Twitter provider.
您的代码现在应该看起来像 chapter09.01-日历
  1. At this point, you should be able to complete a full login using Twitter's OAuth 2 provider. The redirects that occur are as follows, first, we initiate the OAuth 2 provider login as shown in the following screenshot:
读书笔记《spring-security-third-edition》开放 OAuth 2

然后我们被重定向到提供者授权页面,请求用户授予 jbcpcalendar 应用程序的权限,如以下屏幕截图所示:

读书笔记《spring-security-third-edition》开放 OAuth 2
  1. After authorizing the jbcpcalendar application, the user is redirected to the jbcpcalendar application and automatically logged in using the provider display name:
读书笔记《spring-security-third-edition》开放 OAuth 2
  1. At this point, the user exists in the application and is authenticated and authorized with a single GrantedAuthority of ROLE_USER, but if we navigate to My Events, the user will be allowed to view this page. However, no events exist for CalendarUser:
读书笔记《spring-security-third-edition》开放 OAuth 2
  1. Try to create an event for this user to verify that the user credentials that were created correctly in the CalendarUser table.
  2. To verify that the provider details were created correctly, we can open the H2 admin console and query the USERCONNECTION table to verify that standard connection details were saved, as shown in the following screenshot:
读书笔记《spring-security-third-edition》开放 OAuth 2
  1. Additionally, we can verify the CALENDAR_USERS table, which has also been populated with the provider details:
读书笔记《spring-security-third-edition》开放 OAuth 2

现在我们已经在本地数据库中注册了用户,并且我们还能够根据对特定提供商详细信息的授权访问与注册的提供商进行交互。

其他 OAuth 2 提供程序

我们已经使用 Spring Social 的三个当前支持提供者之一成功集成了一个 OAuth 2 提供者。还有其他几个可用的提供程序;我们将添加更多提供商,以便我们的用户拥有多个选项。 Spring Social 目前原生支持 Twitter、Facebook 和 LinkedIn 提供商。包括额外的提供者将需要额外的库来获得这种支持,这将在本章后面介绍。

让我们看一下以下步骤:

  1. In order to add Facebook or LinkedIn providers into the JBCP calendar application, additional application properties need to be set, and each configured provider will automatically be registered with the appId and appSecret keys from the provider application, as follows:
        //src/main/resources/application.yml

        spring:
        social:
         # Twitter
         twitter:
            appId: cgceheRX6a8EAE74JUeiRi8jZ
            appSecret: XR0J2N0Inzy2y2poxzot9oSAaE6MIOs4QHSWzT8dyeZaaeawep
         # facebook
         facebook:
            appId: 299089913898983
            appSecret: 01639f125103752ec408affc92515d0e
         # Linked
            linkedin:
            appId: 866qpyhnq6f6o5
            appSecret: KsFKoOmcGCiLfGfO
  1. We can now add the new login options to our login.html file, and the form.html signup page, to include one new <form> tag for each new provider:
        //src/main/resources/templates/login.html

        <h3>Social Login</h3>
        <form th:action="@{/signin/twitter}" method="POST" class="form-horizontal">
         <input type="hidden" name="scope" value="public_profile" />
         <div class="form-actions">
           <input id="twitter-submit" class="btn" type="submit" value="Login using Twitter"/>
         </div>
        </form>
       <br />
        <form th:action="@{/signin/facebook}" method="POST" class="form-horizontal" <input type="hidden" name="scope" value="public_profile" />
        <div class="form-actions">
            <input id="facebook-submit" class="btn" type="submit" value="Login using Facebook"/>  </div>
       </form>
      <br/>
        <form th:action="@{/signin/linkedin}" method="POST" class="form-horizontal">
        <input type="hidden" name="scope" value="r_basicprofile, r_emailaddress" />
        <div class="form-actions">
           <input id="linkedin-submit" class="btn" type="submit" value="Login using Linkedin"/>
        </div>
      </form>
  1. Now we have the required details to connect to the additional providers for the JBCP calendar, and we can restart the JBCP calendar application and test logging in with the other OAuth 2 providers.
您的代码现在应该看起来像 chapter09.02-日历

现在登录时,我们应该看到额外的提供程序选项,如以下屏幕截图所示:

读书笔记《spring-security-third-edition》开放 OAuth 2

OAuth 2 用户注册问题

如果支持多个提供者,则需要解决的一个问题是返回的各种提供者详细信息之间的用户名冲突。

如果您使用每个列出的提供程序登录 JBCP 日历应用程序(然后查询存储在 H2 中的数据),您会发现根据用户的帐户详细信息,数据可能相似,如果不完全相同的话。

在下面的 USERCONNECTION 表中,我们可以看到来自每个提供程序的 USERID 列数据类似:

读书笔记《spring-security-third-edition》开放 OAuth 2

CALENDARUSER 表中,我们有两个可能的问题。首先,用于 EMAIL 的用户详细信息,即 JBCP 日历用户 ID 不是某些提供商的电子邮件。其次,两个不同提供商的用户标识符仍然可能相同:

读书笔记《spring-security-third-edition》开放 OAuth 2

我们不打算深入探讨检测和纠正这个可能的问题的各种方法,但值得注意以备将来参考。

注册非标准 OAuth 2 提供者

为了包含其他提供程序,我们需要执行一些额外的步骤以将自定义提供程序包含到登录流程中,如下所示:

  1. For each provider, we need to include the provider dependencies in our build.gradle file, as follows:
        //build.gradle

        dependencies {
          ...
          compile("org.springframework.social:spring-social-google:
          ${springSocialGoogleVersion}")
          compile("org.springframework.social:spring-social-github:
          ${springSocialGithubVersion}")
        }
  1. Next, we will register the providers into the JBCP calendar application with the following additional application properties for the appId and appSecret key for each provider:
        //src/main/resources/application.yml

        spring:
          social:
            # Google
               google:
                 appId: 947438796602-uiob88a5kg1j9mcljfmk00quok7rphib.apps.
                 googleusercontent.com
                 appSecret: lpYZpF2IUgNXyXdZn-zY3gpR
           # Github
               github:
                 appId: 71649b756d29b5a2fc84
                 appSecret: 4335dcc0131ed62d757cc63e2fdc1be09c38abbf
  1. Each new provider must be registered by adding the respective ConnectionFactory interface. We can add a new ConnectionFactory entry for each new Provider we intend to support, to the custom DatabaseSocialConfigurer.java file as seen in the following:
        //src/main/java/com/packtpub/springsecurity/configuration/
        DatabaseSocialConfigurer.java

        public class DatabaseSocialConfigurer 
        extends SocialConfigurerAdapter {
           ...
        @Override
        public void addConnectionFactories(
        ConnectionFactoryConfigurer config, Environment env) {
               super.addConnectionFactories(config, env);

            // Adding GitHub Connection with properties
           // from application.yml
               config.addConnectionFactory(
                  new GitHubConnectionFactory(
                    env.getProperty("spring.social.github.appId"),
                       env.getProperty("spring.social.github.appSecret")));
          // Adding Google Connection with properties
         // from application.yml
               config.addConnectionFactory(
               new GoogleConnectionFactory(
               env.getProperty("spring.social.google.appId"),
               env.getProperty("spring.social.google.appSecret")));
             }
         }
  1. We can now add the new login options to our login.html file and form.html sign up page to include one new <form> tag for each new provider:
        //src/main/resources/templates/login.html

        <h3>Social Login</h3>
        ...
        <form th:action="@{/signin/google}" method="POST" class="form-horizontal">
        <input type="hidden" name="scope" value="profile" />
        <div class="form-actions">
           <input id="google-submit" class="btn" type="submit" value="Login using Google"/>
        </div>
      </form>
     <br />

       <form th:action="@{/signin/github}" method="POST" class="form-horizontal">
       <input type="hidden" name="scope" value="public_profile" />
       <div class="form-actions">
         <input id="github-submit" class="btn" type="submit" value="Login using Github"/>
       </div>
     </form&gt;
  1. Now, we have the required details to connect to the additional providers for the JBCP calendar. We can restart the JBCP calendar application and test logging in with the additional OAuth 2 providers. When logging in now, we should be presented with additional provider options, as shown in the following screenshot:
读书笔记《spring-security-third-edition》开放 OAuth 2

OAuth 2 安全吗?

由于对 OAuth 2 的支持依赖于 OAuth 2 提供者的可信度和提供者​​响应的可验证性,因此安全性和真实性对于应用程序对用户基于 OAuth 2 的登录有信心至关重要。

幸运的是,OAuth 2 规范的设计者非常清楚这一问题,并实施了一系列验证步骤来防止响应伪造、重放攻击和其他类型的篡改,具体解释如下:

  • Response forgery is prevented due to a combination of a shared secret key (created by the OAuth 2-enabled site prior to the initial request), and a one-way hashed message signature on the response itself. A malicious user tampering with the data in any of the response fields without having access to the shared secret key—and signature algorithm—would generate an invalid response.
  • Replay attacks are prevented due to the inclusion of a nonce, or a one-time use, random key, which should be recorded by the OAuth 2-enabled site so that it cannot ever be reused. In this way, even a user attempting to reissue the response URL would be foiled because the receiving site would determine that the nonce had been previously used, and would invalidate the request.
  • The most likely form of attack that could result in a compromised user interaction would be a man-in-the-middle attack, where a malicious user could intercept the user's interaction between their computer and the OAuth 2 provider. A hypothetical attacker in this situation could be in a position to record the conversation between the user's browser and the OAuth 2 provider, and record the secret key used when the request was initiated. The attacker, in this case, would need a very high level of sophistication and reasonably a complete implementation of the OAuth 2 signature specification—in short, this is not likely to occur with any regularity.

概括

在本章中,我们回顾了 OAuth 2,这是一种相对较新的用户身份验证和凭据管理技术。 OAuth 2 在网络上具有非常广泛的影响力,并且在过去一两年内在可用性和接受度方面取得了长足的进步。现代网络上大多数面向公众的网站都应该计划提供某种形式的 OAuth 2 支持,JBCP 日历应用程序也不例外!

在本章中,我们了解了 OAuth 2 身份验证机制及其高级架构和关键术语的以下主题。我们还了解了使用 JBCP 日历应用程序的 OAuth 2 登录和自动用户注册。

我们还介绍了使用 OAuth 2 的自动登录以及 OAuth 2 登录响应的安全性。

我们介绍了使用 Spring Security 实现的最简单的单点登录机制之一。缺点之一是它不支持单次注销的标准机制。在下一章中,我们将探讨 CA​​S,另一种标准的单点登录协议,它也支持单点注销。