# spring-security-oauth2-demo **Repository Path**: cm_fighting/spring-security-oauth2-demo ## Basic Information - **Project Name**: spring-security-oauth2-demo - **Description**: spring security + ouath2 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-10-21 - **Last Updated**: 2022-10-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## Oauth2认证 ### 1 简介 第三方认证技术方案最主要的就是解决认证协议的通用标准问题,因为要实现跨系统认证,各系统之间要遵循一定的接口协议。 Oauth协议为用户资源的授权提供了一个安全的、开放而又简易的标准。同时任何第三方都可以使用Oauth认证服务,任何服务提供商都可以实现自身的oauth认证服务,因而oauth是开放的,业界提供了OAUTH的多种实现,大大节约了开发时间,所以oauth是简易的。很多大公司都提供了oauth认证服务,足以说明oauth标准主健成为开放资源授权的标准 Oauth协议目前广泛使用的是2.0版本。 #### 1.1 角色 - 客户端:本身不存储资源,需要资源拥有者的授权去请求资源服务器的资源 - 资源拥有者:即用户 - 授权服务器:用来对资源拥有者的身份进行认证、对访问资源进行授权 - 资源服务器:存储资源的服务器 如我们的网站需要通过微信登录,那么客户端就是我们的网站,资源拥有者就是用户,授权服务器就是微信的认证服务器,登录网站是用户通过扫码就是授权的过程,微信会对用户进行认证,然后授权确认后对访问资源授权,资源服务器存储的就是我们授权后想要获取到的微信用户身份信息,如头像、微信名等。 #### 1.2 常用术语 - 客户凭证:client Credentials,客户端的clientId和密码用于认证客户 - 令牌:tokens,授权服务器在接收到客户请求后,颁发的访问令牌 - 作用域:scopes,客户请求访问令牌时,由资源拥有者额外指定的细分权限(permission) #### 1.3 令牌类型 1. 授权码:仅用于授权码授权类型,用户交换获取访问令牌和刷新令牌 2. 访问令牌:用于代表一个用户或服务直接去访问受保护的资源 3. 刷新令牌:用于去授权服务器获取一个刷新的访问令牌 4. BearerToken:不管谁拿到Token都可以访问资源 5. Proof of Possession(PoP) Token:可以校验client是否对Token有明确的拥有权 #### 1.4 优缺点 **优点** 1. 更安全,客户端不需要接触到用户密码,服务端更集中保护 2. 广泛传播并被持续采用 3. 短寿命和封装的token 4. 资源服务器和授权服务器解耦 5. 集中式授权、简化客户端 6. HTTP/JSON友好,易于请求和传递token 7. 考虑到多种客户端架构场景 8. 客户可以具有不同的信任级别 **缺点** 1. 协议框架太宽泛,造成各种实现的兼容性和互操作性差 2. 不是一个认证协议,本身并不能告诉你任何用户信息 ### 2. 运行流程 ![运行流程](images/img_4.png) - (A)用户打开客户端以后,客户端要求用户给予授权。 - (B)用户同意给予客户端授权。 - (C)客户端使用上一步获得的授权,向认证服务器申请令牌。 - (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。 - (E)客户端使用令牌,向资源服务器申请获取资源。 - (F)资源服务器确认令牌无误,同意向客户端开放资源。 ### 3 授权模式 #### 3.1 授权码模式 ![授权码模式](images/img.png) #### 3.2 简化授权模式 ![简化模式](images/img_1.png) #### 3.3 密码模式 ![密码模式](images/img_2.png) #### 3.4 客户端模式 ![客户端模式](images/img_3.png) **刷新令牌** 如果用户访问的时候,客户端的"访问令牌"已经过期,则需要使用"更新令牌"申请一个新的访问令牌。 客户端发出更新令牌的HTTP请求,包含以下参数: - grant_type:表示使用的授权模式,此处的值固定为"refresh_token",必选项。 - refresh_token:表示早前收到的更新令牌,必选项。 - scope:表示申请的授权范围,不可以超出上一次申请的范围,如果省略该参数,则表示与上一次一致。 示例: ```bash POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA ``` ### 4. Spring Security Oauth2 #### 4.1 授权服务器 ![授权服务器](images/img_5.png) - Authorize Endpoint:授权端点,进行授权 - Token Endpoint:令牌端点,授权后拿到对应的Token - Introspection Endpoint:校验端点,检验Token的合法性 - Revocation Endpoint:撤销端点,撤销授权 #### 4.2 Spring Security Oauth2架构 ![Spring Security Oauth2架构](images/img_6.png) 1. 用户访问,首次访问没有Token,OauthRestTemplate会报错,这个报错信息会被Oauth2ClientContextFilter捕获并重定向到认证服务器 2. 认证服务器通过Authorization Endpoint进行授权,并通过AuthorizationServerTokenServices生成授权码返回给客户端 3. 客户端拿到授权码去认证服务器通过Token Endpoint调用AuthorizationServerTokenServices生成Token并返回给客户端 4. 客户端拿到Token去资源服务器访问资源,一般会通过Oauth2AuthenticationManager调用ResourceServerTokenServices进行校验。校验通过可以获取资源。 ### 5. 代码测试 #### 5.1 授权码模式 1. 引入依赖:spring cloud以及security、oauth2相关依赖 2. 配置自定义登录逻辑 3. 配置security,关闭csrf以及放行oauth和登录登出相关路径 4. 配置授权服务器配置,配置授权模式为授权码模式 5. 配置资源服务器配置 **测试:** - 获取授权码,参数要和配置授权服务器的配置相同 ```bash http://127.0.0.1:8080/oauth/authorize?response_type=code&client_id=admin&redirect_uri=https://www.baidu.com&scope=all ``` - 获取token 登录成功后 在oauth授权页面选择允许授权 ,会根据我们配置的重定向地址跳转,先不关系,看后面跟着的code就是授权码,拿到这个授权码,发送Post请求,认证方式选择Basic Auth,请求体选择 x-www-form-urlencoded,其中code就是上一步获取的授权码 ![获取token1](images/img_7.png) ![获取token2](images/img_8.png) - 根据获取的token请求资源服务器获取开放的资源 ![获取资源](images/img_9.png) #### 5.2 密码模式 1. 授权服务器配置中指定模式为密码模式 2. 重写configure(AuthorizationServerEndpointsConfigurer endpoints) **测试** 1. 不需要请求oauth获取授权码了,直接发送post请求,获取token,认证方式依然是Basic Auth 2. 传参中授权模式传password,另外不需要授权码了,而是传递username和password 3. 拿到token就可以去资源服务器请求资源了 ![密码模式获取token](images/img_10.png) #### 5.3 token存储到redis 1. token默认存储在内存中,通过配置tokenStore来自定义存储方式 2. 调用spring security oauth依赖中提供的RedisToken生成一个TokenStore对象并注入成Bean 3. 授权服务器配置中指定tokenStore为redisTokenStore #### 5.4 将默认的token替换成jwt 1. jwt自带一些校验方式,所以不需要redis了 2. 和redisTokenStore类似,oauth依赖包中也提示了jwtTokenStore,直接注入再在授权配置中指定即可 3. 不同于redisStore直接将oauth存储到redis中,这里需要将oauth的token转换成jwt格式的token,所以在生成jwtTokenStore时需要指定一个转换器,也是自己注入一个,转换器中设置一个jwt生成时的盐值。 4. 再次测试,返回的access_token已经是jwt格式的了 ![获取jwt格式token](images/img_11.png) #### 5.5 jwt内容增强器 配置jwt内容增强器,即扩展jwt荷载中的内容,加入一些自定义数据。 1. 定义JwtTokenEnhance,设置添加自定义数据 2. 注册为Bean,在授权配置中引入,并由此定义token增强链,然后设置到授权端点配置中 3. 测试访问oauth获取token,解析可以看到自定义数据 ![jwt token增强](images/img_12.png) #### 5.6 解析请求头携带过来的token 我们已经将token定义为jwt格式的token,且配置了jwt token增强器,所以我们可以通过请求头中Authorization值拿到token数据,然后解析出来,一般会在过滤器中解析,通过解析判断是否过期或者根据用户名来进一步查询相关操作是否拥有指定权限等,这个根据需求来自定义处理逻辑,这里我们直接测试解析数据。 1. 引入jjwt依赖 2. 通过Jwts.parser方法和生成token时设置的盐来获取Claims对象 ![解析jwt数据](images/img_13.png) #### 5.7 刷新令牌 刷新令牌很简单,即请求获取token的时候,除了返回access_token还多返回一个refresh_token,当请求资源提示token过期后,不再需要使用密码模式中的用户名密码,或者授权码模式中的授权码重新请求获取token,而是直接根基刷新token就可以拿到一个新的access_token和新的refresh_token了。相关配置也很简单,授权服务器配置中配置授权模式,支持配置多个参数,原基础上加了刷新令牌模式即可(refresh_token) 根据刷新令牌获取新的token,授权模式传refresh_token,再传递一个refresh_token,其他参数就都不用传了,如图 ![刷新令牌](images/img_14.png)