Spring Security OAuth2 授权码模式的实现

写在前边

在文章OAuth 2.0 概念及授权流程梳理 中我们谈到OAuth 2.0的概念与流程,这里我准备分别记一记这几种授权模式的demo,一方面为自己的最近的学习做个总结,另一方面做下知识输出,如果文中有错误的地方,请评论指正,在此不胜感激

受众前提

阅读本文,默认读者已经过Spring Security有一定的了解,对OAuth2流程有一定了解

本文目标

带领读者对Spring Security OAuth2框架的授权码模式有一个比较直观的概念,能使用框架搭建授权码模式授权服务器与资源服务器(分离版本)

授权码模式流程回顾

授权码模式要求:用户登录并对第三方应用(客户端)进行授权,出示授权码交给客户端,客户端凭授权码换取access_token(访问凭证)

此模式要求授权服务器与用户直接交互,在此过程中,第三方应用是无法获取到用户输入的密码等信息的,这个模式也是OAuth 2.0中最安全的一个

Demo基本结构

这里主要关注authorization-code-authorization-serverauthorization-code-resource-server这两个模块

本文以及后续文章的demo均放在GitHub上,欢迎大家Star & Fork,源码地址:https://github.com/hellxz/spring-security-oauth2-learn

authorization-code-client-resttemplate-jdbc这个项目是用来测试非OAuth2服务使用RestTemplate与JdbcTemplate对接OAuth2授权服务的,流程这里不讲,有兴趣可以debug看看,可能会让您对整个流程会有更清晰的感受

Maven依赖

 <!--Spring Security-->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <!--Spring Boot Starter Web 所有demo均使用web-->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <!-- Spring Security OAuth2 -->
  <dependency>
   <groupId>org.springframework.security.oauth</groupId>
   <artifactId>spring-security-oauth2</artifactId>
   <version>${spring-security-oauth2.version}</version>
  </dependency>

搭建授权服务器(Authorization Server)

文中服务器均使用demo级别配置,请勿直接使用到生产环境

授权服务器结构主体如下:

启动类自不多说,先说下SecurityConfig

package com.github.hellxz.oauth2.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.Collections;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

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

 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  // @formatter: off
  auth.inMemoryAuthentication()
    .withUser("hellxz")
    .password(passwordEncoder().encode("xyz"))
    .authorities(Collections.emptyList());
  // @formatter: on
 }

 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http.authorizeRequests()
    .anyRequest().authenticated() //所有请求都需要通过认证
    .and()
    .httpBasic() //Basic登录
    .and()
    .csrf().disable(); //关跨域保护
 }
}

通过@Configuration@EnableWebSecurity开启Spring Security配置,继承WebSecurityConfigurerAdapter的方法,实现个性化配置,这里我们使用内存保存一个名为hellxz、密码为xyz的用户,与授权服务器交互的用户就是他了

除了配置用户,我们需要对服务的资源进行保护,这里将所有的请求都要求通过认证才可以访问,用户登录需要使用httpBasic形式(就是那种网页弹个窗要求登录的那种😄)

Spring Security 5.x版本后,要求显示声明使用的密码器,就是PasswordEncoder了,常用BCryptPasswordEncoder,简单的可以认为它是使用时间戳和盐进行加密的一种算法,同一个密码被加密后也不会相同

接着看看授权服务器的配置,画重点

package com.github.hellxz.oauth2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;

//授权服务器配置
@Configuration
@EnableAuthorizationServer //开启授权服务
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {

 @Autowired
 private PasswordEncoder passwordEncoder;

 @Override
 public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
  //允许表单提交
  security.allowFormAuthenticationForClients()
    .checkTokenAccess("isAuthenticated()");
 }

 @Override
 public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  // @formatter: off
  clients.inMemory()
    .withClient("client-a") //client端唯一标识
     .secret(passwordEncoder.encode("client-a-secret")) //客户端的密码,这里的密码应该是加密后的
     .authorizedGrantTypes("authorization_code") //授权模式标识
     .scopes("read_user_info") //作用域
     .resourceIds("resource1") //资源id
     .redirectUris("http://localhost:9001/callback"); //回调地址
  // @formatter: on
 }
}

1.通过@ConfigurationEnableAuthorizationServer开启授权服务器配置,通过重写AuthorizationServerConfigurerAdapter的方法来完成自定义授权服务器

2.OAuth2授权码模式中,要求不仅仅用户需要登录,还要求客户端也需要登录,这里就需要在configure(ClientDetailsServiceConfigurer clients)这个方法中配置客户端(第三方应用)的登录信息,

withClient中配置的是客户端id(client_id)
secret为客户端的密码,要求使用加密器进行加密
授权码的authorizedGrantTypes必须配置有"authorization_code"(授权码模式),这里是可以同时支持多种授权模式的,为了简单只写一个
scopes,请求资源作用域,用于限制客户端与用户无法访问没有作用域的资源
resourceIds,可选,资源id,可以对应一个资源服务器,个人理解为某个资源服务器的所有资源标识
redirectUris,回调地址,有两个作用:1.回调客户端地址,返回授权码; 2.校验是否是同一个客户端

redirectUris校验是否同一个客户端这个,可能说的不是很准确,说下大体流程,我们在授权服务器上配置了这个回调地址,授权服务器在用户授权成功后,返回授权码的地址就是它,另外我们后续申请token时,也需要传递这个回调地址,所以我的理解是校验是否是同一客户端发来的第二次请求(换token时)

3.configure(AuthorizationServerSecurityConfigurer security)这里配置资源客户端(第三方应用)的表单提交权限,类似Spring Security配置的permitAll()等权限控制标识,如果不配置,客户端将无法换取token

4.application.properties

这里我只配置了server.port=8080

这样我们就配置了相当简易的授权服务器,启动测试下

获取授权码的流程,一般是由客户端使用自己的client_id与密码+response_type=code拼接url,让浏览器跳转完成的,用户的登录与授权过程都需要在浏览器中完成,启动项目后访问下列url

http://localhost:8080/oauth/authorize?client_id=client-a&client_secret=client-a-secret&response_type=code

登录用户/密码: hellxz/xyz ,选择Approve表示接受授权,Deny反之,如下动图所示

最后我们得到了回调地址http://localhost:9001/callback?code=2e6450

这里的code就是授权码,接下来我们使用授权码进行换取token

POST请求,http://localhost:8080/oauth/token,参数如图

BasicAuth:这里填的是客户端配置的client_id和client_secret的值,相当于curl --user client_id:client_secret,配置后会在Header中添加Authorization:Basic Y2xpZW50LWE6Y2xpZW50LWEtc2VjcmV0Basic空格 后的是client_id:client_secret具体值被Base64后得到的值

请求参数列表:

  • code=授权码
  • grant_type=authorization_code
  • redirect_uri=回调url ,要与配置处和获取授权码处相同
  • scope=作用域

最后我们获得了授权服务的响应,包含token的json

{
 "access_token": "99435e13-f9fe-438a-a94e-3b00d549b329", //访问token
 "token_type": "bearer", //token类型,使用时需要拼接在token前并在token前加空格
 "expires_in": 43199, //过期时间
 "scope": "read_user_info" //作用域
}

在access_token未过期之前,同一个用户名使用同一个客户端访问都会是同一个access_token

授权服务器先放在这里,不要关服,接下来搭建资源服务器

搭建资源服务器(Resource Server)

资源服务器结构

入口类不多说,先搭建资源服务器主要配置,这里直接使用ResourceConfig进行配置

package com.github.hellxz.oauth2.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;

@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {

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

 @Primary
 @Bean
 public RemoteTokenServices remoteTokenServices() {
  final RemoteTokenServices tokenServices = new RemoteTokenServices();
  //设置授权服务器check_token端点完整地址
  tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
  //设置客户端id与secret,注意:client_secret值不能使用passwordEncoder加密!
  tokenServices.setClientId("client-a");
  tokenServices.setClientSecret("client-a-secret");
  return tokenServices;
 }

 @Override
 public void configure(HttpSecurity http) throws Exception {
  //设置创建session策略
  http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
  //@formatter:off
  //所有请求必须授权
  http.authorizeRequests()
    .anyRequest().authenticated();
  //@formatter:on
 }

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

1.通过@Configuration@EnableResourceServer这两个注解标识服务是一个资源服务器,重写ResourceServerConfigurerAdapter来实现自定义授权服务器

2.配置configure(HttpSecurity http)方法,这里可以代替Spring Security同名方法配置,开启所有请求需要授权才可访问

3.配置资源相关设置configure(ResourceServerSecurityConfigurer resources),这里只设置resourceId

后续的使用redis校验token也在这里设置

4.校验token的配置,这里使用了远程调用授权服务器帮忙校验token的方式,只需要显示注入RemoteTokenServices remoteTokenServices()的Bean,就可以调用授权服务器的/oauth/check_token端点,设置客户端配置的值,详见注释

这样一来我们就配置好了资源服务器,当然光有配置是不够的,我们搞一个资源接口做测试用

上边的ResourceControllerUserVO都比较简单,传入一个名称,返回用户对象,包含用户名和邮箱信息

package com.github.hellxz.oauth2.web.controller;

import com.github.hellxz.oauth2.web.vo.UserVO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ResourceController {

 @GetMapping("/user/{username}")
 public UserVO user(@PathVariable String username){
  return new UserVO(username, username + "@foxmail.com");
 }
}
package com.github.hellxz.oauth2.web.vo;

public class UserVO {
 private String username;
 private String email;

 public UserVO(String username, String email) {
  this.username = username;
  this.email = email;
 }

 public String getUsername() {
  return username;
 }

 public void setUsername(String username) {
  this.username = username;
 }

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }
}

application.properties中配置了与授权服务器不同的端口:8081

server.port=8081

启动资源服务测试

什么也不传,直接访问接口,提示资源需要授权

复制之前获取到的token,添加token访问接口http://localhost:8081/user/hellxz001

Bearer Token相当于在Headers中添加Authorization:Bearer空格access_token

至此我们成功的搭建并测试了授权码模式下的最简单的授权服务与资源服务分离的demo

尾声

授权码模式就先在这里告一段落,写的比较基础,自认为该说到的点都说到了,后续还会写其它模式的文章,如文中有何遗漏,请不吝评论反馈,本人会尽快改正,谢谢

本文以及后续文章的demo均放在GitHub上,欢迎大家Star & Fork,源码地址:https://github.com/hellxz/spring-security-oauth2-learn ,Demo中的README文档写得比较详细,也可堪一看

原文出处https://www.cnblogs.com/hellxz/p/oauth2_oauthcode_pattern.html

到此这篇关于Spring Security OAuth2 授权码模式的实现的文章就介绍到这了,更多相关Spring Security OAuth2 授权码模式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 基于Spring Security的Oauth2授权实现方法

    前言 经过一段时间的学习Oauth2,在网上也借鉴学习了一些大牛的经验,推荐在学习的过程中多看几遍阮一峰的<理解OAuth 2.0>,经过对Oauth2的多种方式的实现,个人推荐Spring Security和Oauth2的实现是相对优雅的,理由如下: 1.相对于直接实现Oauth2,减少了很多代码量,也就减少的查找问题的成本. 2.通过调整配置文件,灵活配置Oauth相关配置. 3.通过结合路由组件(如zuul),更好的实现微服务权限控制扩展. Oauth2概述 oauth2根据使用场景不同

  • Spring Security OAuth2实现使用JWT的示例代码

    1.概括 在博客中,我们将讨论如何让Spring Security OAuth2实现使用JSON Web Tokens. 2.Maven 配置 首先,我们需要在我们的pom.xml中添加spring-security-jwt依赖项. <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> &l

  • 使用Spring Security OAuth2实现单点登录

    1.概述 在本教程中,我们将讨论如何使用Spring Security OAuth和Spring Boot实现SSO - 单点登录. 我们将使用三个单独的应用程序: •授权服务器 - 这是中央身份验证机制 •两个客户端应用程序:使用SSO的应用程序 非常简单地说,当用户试图访问客户端应用程序中的安全页面时,他们将被重定向到首先通过身份验证服务器进行身份验证. 我们将使用OAuth2中的授权代码授权类型来驱动身份验证委派. 2.客户端应用程序 让我们从客户端应用程序开始;当然,我们将使用Sprin

  • Spring Security OAuth2 token权限隔离实例解析

    这篇文章主要介绍了Spring Security OAuth2 token权限隔离实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 由于项目OAuth2采用了多种模式,授权码模式为第三方系统接入,密码模式用于用户登录,Client模式用于服务间调用, 所有不同的模式下的token需要用 @PreAuthorize("hasAuthority('client')") 进行隔离,遇到问题一直验证不通过. 通过调试发现资源服务从授权服

  • Spring Security OAuth2认证授权示例详解

    本文介绍了如何使用Spring Security OAuth2构建一个授权服务器来验证用户身份以提供access_token,并使用这个access_token来从资源服务器请求数据. 1.概述 OAuth2是一种授权方法,用于通过HTTP协议提供对受保护资源的访问.首先,OAuth2使第三方应用程序能够获得对HTTP服务的有限访问权限,然后通过资源所有者和HTTP服务之间的批准交互来让第三方应用程序代表资源所有者获取访问权限. 1.1 角色 OAuth定义了四个角色 资源所有者 - 应用程序的

  • Spring Security Oauth2.0 实现短信验证码登录示例

    本文介绍了Spring Security Oauth2.0 实现短信验证码登录示例,分享给大家,具体如下: 定义手机号登录令牌 /** * @author lengleng * @date 2018/1/9 * 手机号登录令牌 */ public class MobileAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecur

  • Spring Security OAuth2集成短信验证码登录以及第三方登录

    前言 基于SpringCloud做微服务架构分布式系统时,OAuth2.0作为认证的业内标准,Spring Security OAuth2也提供了全套的解决方案来支持在Spring Cloud/Spring Boot环境下使用OAuth2.0,提供了开箱即用的组件.但是在开发过程中我们会发现由于Spring Security OAuth2的组件特别全面,这样就导致了扩展很不方便或者说是不太容易直指定扩展的方案,例如: 图片验证码登录 短信验证码登录 微信小程序登录 第三方系统登录 CAS单点登录

  • Spring Security OAuth2 实现登录互踢的示例代码

    本文主要介绍了Spring Security OAuth2 实现登录互踢的示例代码,分享给大家,具体如下: 背景说明 一个账号只能一处登录,类似的业务需求在现有后管类系统是非常常见的. 但在原有的 spring security oauth2 令牌方法流程(所谓的登录)无法满足类似的需求. 我们先来看 TokenEndpoint 的方法流程 客户端 带参访问 /oauth/token 接口,最后去调用 TokenGranter TokenGranter 根据不同的授权类型,获取用户认证信息 并去

  • spring-boot集成spring-security的oauth2实现github登录网站的示例

    spring-security 里自带了oauth2,正好YIIU里也用到了spring-security做权限部分,那为何不直接集成上第三方登录呢? 然后我开始了折腾 注意:本篇只折腾了spring-security oauth2的客户端部分,spring-security还可以搭建标准的oauth2服务端 引入依赖 <dependency> <groupId>org.springframework.security.oauth</groupId> <artif

  • Spring Security OAuth2 授权码模式的实现

    写在前边 在文章OAuth 2.0 概念及授权流程梳理 中我们谈到OAuth 2.0的概念与流程,这里我准备分别记一记这几种授权模式的demo,一方面为自己的最近的学习做个总结,另一方面做下知识输出,如果文中有错误的地方,请评论指正,在此不胜感激 受众前提 阅读本文,默认读者已经过Spring Security有一定的了解,对OAuth2流程有一定了解 本文目标 带领读者对Spring Security OAuth2框架的授权码模式有一个比较直观的概念,能使用框架搭建授权码模式授权服务器与资源服

  • 使用JWT作为Spring Security OAuth2的token存储问题

    目录 序 授权服务器整合JWT--对称加解密算法 资源服务器整合JWT--对称加解密算法 OAuth整合JWT--非对称加解密RSA 测试验证 测试通过 序 Spring Security OAuth2的demo在前几篇文章中已经讲过了,在那些模式中使用的都是RemoteTokenService调用授权服务器来校验token,返回校验通过的用户信息供上下文中获取 这种方式会加重授权服务器的负载,你想啊,当用户没授权时候获取token得找授权服务器,有token了访问资源服务器还要访问授权服务器,

  • Spring Security 自定义授权服务器实践记录

    目录 前言 授权服务器变迁 最小化配置 安装授权服务器 配置授权服务器 配置客户端 体验 总结 前言 在之前我们已经对接过了GitHub.Gitee客户端,使用OAuth2 Client能够快速便捷的集成第三方登录,集成第三方登录一方面降低了企业的获客成本,同时为用户提供更为便捷的登录体验.但是随着企业的发展壮大,越来越有必要搭建自己的OAuth2服务器.OAuth2不仅包括前面的OAuth客户端,还包括了授权服务器,在这里我们要通过最小化配置搭建自己的授权服务器.授权服务器主要提供OAuth

随机推荐