我们在上文讲了如何在spring security的环境中搭建基于oauth2协议的认证中心demo:https://www.cnblogs.com/process-h/p/15688971.html, 对应的还应该要有一个resource server。本章主要就是讲resource server的demo搭建,并且再将普通的token改造成JWT令牌的形式以及为什么要改成JWT令牌格式。

自定义resource-server实现类

​ 在搭建oauth2认证中心时,我们需要再自定义一个继承AuthorizationServerConfigurerAdapter的实现类,同时在该实现类上添加@EnableAuthorizationServer注解。同样的,我们也需要创建一个继承ResourceServerConfigurerAdapter的实现类,并在该实现类上添加@EnableResourceServer注解来声明这是一个资源服务。

  1. @Configuration
  2. @EnableResourceServer
  3. public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
  4. }

​ 我们来看看ResourceServerConfigurerAdapter父类,发现它重写了2个方法。以下是这2个方法中的参数对象可以配置的属性:

ResourceServerSecurityConfigurer中主要包括:

  • tokenServices:ResourceServerTokenServices 类的实例,用来实现令牌服务。
  • tokenStore:TokenStore类的实例,指定令牌如何访问。
  • resourceId:这个资源服务的ID,这个属性是可选的,但是推荐设置并在授权服务中进行验证。
  • 其他的拓展属性例如 tokenExtractor 令牌提取器用来提取请求中的令牌。

HttpSecurity配置这个与Spring Security类似:

  • 请求匹配器,用来设置需要进行保护的资源路径,默认的情况下是保护资源服务的全部路径。
  • 通过http.authorizeRequests()来设置受保护资源的访问规则。
  • 其他的自定义权限保护规则通过 HttpSecurity 来进行配置。
  1. public class ResourceServerConfigurerAdapter implements ResourceServerConfigurer {
  2. @Override
  3. public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
  4. }
  5. @Override
  6. public void configure(HttpSecurity http) throws Exception {
  7. http.authorizeRequests().anyRequest().authenticated();
  8. }
  9. }

编写ResourceServerConfig

  1. package com.peter.security.order.config;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  5. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  6. import org.springframework.security.config.http.SessionCreationPolicy;
  7. import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
  8. import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
  9. import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
  10. import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
  11. import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
  12. /**
  13. * @author huang
  14. * @description
  15. * @date 2021/12/17
  16. **/
  17. @Configuration
  18. @EnableResourceServer
  19. @EnableGlobalMethodSecurity(prePostEnabled = true)
  20. public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
  21. public static final String RESOURCE_ID = "res1";
  22. //资源服务令牌解析服务
  23. @Bean
  24. public ResourceServerTokenServices tokenService() {
  25. //使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
  26. RemoteTokenServices service=new RemoteTokenServices();
  27. service.setCheckTokenEndpointUrl("http://localhost:53020/uaa/oauth/check_token");
  28. // 声明只有该client 的接入方才能访问该资源服务
  29. service.setClientId("c1");
  30. service.setClientSecret("secret");
  31. return service;
  32. }
  33. @Override
  34. public void configure(ResourceServerSecurityConfigurer resources) {
  35. // 声明该资源服务id,以及认证的tokenSerivce对象
  36. resources.resourceId(RESOURCE_ID)
  37. .tokenServices(tokenService());
  38. }
  39. @Override
  40. public void configure(HttpSecurity http) throws Exception {
  41. http
  42. .authorizeRequests()
  43. // 访问该资源服务的client的scope要有all权限
  44. .antMatchers("/**").access("#oauth2.hasScope('all')")
  45. .and().csrf().disable()
  46. .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
  47. }
  48. }

定义资源服务接口

​ 有了配置,还需要暴露出一些资源接口给外部调用,我们这里简单定义一个接口。

  1. package com.peter.security.order.controller;
  2. import org.springframework.security.access.prepost.PreAuthorize;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.RestController;
  5. /**
  6. * @author huangyizeng
  7. * @description
  8. * @date 2021/12/17
  9. **/
  10. @RestController
  11. public class OrderController {
  12. // 访问该资源的用户要有P1权限
  13. @GetMapping(value = "/r1")
  14. @PreAuthorize("hasAnyAuthority('p1')")
  15. public String r1(){
  16. return "访问资源1";
  17. }
  18. }

示例

1、申请token

​ 我们知道Oauth2协议有4中授权方式,分别是:

http://localhost:53020/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=hyz&password=hyz

2、查看token代表了哪些信息

​ spring security oauth2 中有个用于资源服务访问的令牌解析端点(/oauth/check_token )。我们先来看看上面返回的token中都有哪些权限,访问

http://localhost:53020/uaa/oauth/check_token?token=c46ffcf5-3043-4513-9405-808f89b02f30

​ 可以看到返回以下属性信息:

  • 第三方client的基本信息:user_name\client_Id\scope
  • 第三方client的授权列表:authorities
  • 第三方client的资源服务列表:res1

如果我们传入一个不存在的token,那么将会返回

  1. {
  2. "error": "invalid_token",
  3. "error_description": "Token was not recognised"
  4. }

3、使用token访问资源服务

​ 最后我们再来使用该token访问资源服务。oauth2.0 要求将token根据具体的格式放在请求头中:Authorization: Bearer token

​ 如上图,资源服务在接收到token参数后,会到授权服务的/oauth/check_token端点拿到该token的权限,然后与资源服务定义的授权进行比对,如果符合就继续执行,否则返回

  1. {
  2. "error": "invalid_token",
  3. "error_description": "c46ffcf5-3043-4513-9405-808f89b02f301"
  4. }

JWT令牌

​ 通过上面的测试,当资源服务和授权服务不在一起时,资源服务需要通过网络请求去授权服务的/token/check_token端点请求验证token,如果访问量较大将会影响系统的性能。

​ 为了解决性能问题,可以将token令牌采用jwt格式,这样用户授权后通过后将会拿到一个JWT令牌,JWT令牌中已经包含了用户相关的信息,包括权限,基本信息等,接着资源服务根据事先跟认证服务约定好的算法自行进行令牌校验,无需每次都想认证服务请求验证。

​ JWT具体是什么,相信童鞋们也有一定的认识了,这里不过多说明。主要说明一点,一般JWT是是用非对称加密使用的,在这里我们使用对称加密的方式,省去非对称加密密钥生成步骤。

1、配置认证服务JWT令牌

​ 在认证服务中配置JWT令牌,即可实现生成JWT格式的令牌。

TokenConfig ,修改tokenService的实现类。

  1. @Configuration
  2. public class TokenConfig {
  3. private String SIGNING_KEY = "uaa123";
  4. @Bean
  5. public TokenStore tokenStore() {
  6. return new JwtTokenStore(accessTokenConverter());
  7. }
  8. @Bean
  9. public JwtAccessTokenConverter accessTokenConverter() {
  10. JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
  11. converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
  12. return converter;
  13. }
  14. }

定义使用JWT令牌

  1. public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
  2. @Autowired
  3. private JwtAccessTokenConverter accessTokenConverter;
  4. @Bean
  5. public AuthorizationServerTokenServices tokenService() {
  6. DefaultTokenServices service=new DefaultTokenServices();
  7. service.setClientDetailsService(clientDetailsService);
  8. service.setSupportRefreshToken(true);
  9. // 定义使用JWT令牌
  10. service.setTokenStore(tokenStore);
  11. TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
  12. tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
  13. service.setTokenEnhancer(tokenEnhancerChain);
  14. service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
  15. service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
  16. return service;
  17. }
  18. }

检验令牌,根据密码授权模式来获取令牌:

​ 由上图可见,生成的令牌确实已经是JWT的格式了。

​ 再查看该令牌中代表了哪些信息。访问

http://localhost:53020/uaa/oauth/check_token?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJ7XCJmdWxsbmFtZVwiOlwiaHl6XCIsXCJpZFwiOlwiMVwiLFwicGFzc3dvcmRcIjpcIiQyYSQxMCRJdm94aUN3eEdXWkdFemxkcVY2bktPcUxuY05zMmNVdkdsQ2ZmZ1V0a2hZaGc4dUo0akd5eVwiLFwidXNlcm5hbWVcIjpcImh5elwifSIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2Mzk4MTk4ODgsImF1dGhvcml0aWVzIjpbInAxIiwicDMiXSwianRpIjoiNDAzNWU3NTItNmYzNS00Y2RhLTg0ZTgtNjEyZmFhZWI2OWFkIiwiY2xpZW50X2lkIjoiYzEifQ.VIzqACiYJ-ZCTrq1BkOM_HeXkTwE9tAZ3TSiy7B9pJ4

确实访问了在上面实例中的client相关信息:

  1. {
  2. "aud": [
  3. "res1"
  4. ],
  5. "user_name": "{\"fullname\":\"hyz\",\"id\":\"1\",\"password\":\"$2a$10$IvoxiCwxGWZGEzldqV6nKOqLncNs2cUvGlCffgUtkhYhg8uJ4jGyy\",\"username\":\"hyz\"}",
  6. "scope": [
  7. "all"
  8. ],
  9. "exp": 1639819888,
  10. "authorities": [
  11. "p1",
  12. "p3"
  13. ],
  14. "jti": "4035e752-6f35-4cda-84e8-612faaeb69ad",
  15. "client_id": "c1"
  16. }

​ 由此可见,基于JWT的改造已经完成。

2、修改资源服务检验令牌配置

资源服务需要和授权服务拥有一致的签字、令牌服务等:

  1. 将认证服务中的TokenConfig类拷贝到资源 服务中
  2. 将授权服务中的TokenConfig类拷贝到资源 服务中
  1. public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
  2. public static final String RESOURCE_ID = "res1";
  3. @Autowired
  4. private TokenStore tokenStore;
  5. //资源服务令牌解析服务
  6. // @Bean
  7. // public ResourceServerTokenServices tokenService() {
  8. // //使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
  9. // RemoteTokenServices service=new RemoteTokenServices();
  10. // service.setCheckTokenEndpointUrl("http://localhost:53020/uaa/oauth/check_token");
  11. // service.setClientId("c1");
  12. // service.setClientSecret("secret");
  13. // return service;
  14. // }
  15. @Override
  16. public void configure(ResourceServerSecurityConfigurer resources) {
  17. resources.resourceId(RESOURCE_ID)
  18. .tokenStore(tokenStore);
  19. }
  20. }

验证令牌是否有效

​ 我们再次使用上面基于JWT生成的令牌去访问资源服务:

​ 由上图可知是可以正常访问的,如果该令牌有误,则会返回

  1. {
  2. "error": "invalid_token",
  3. "error_description": "Cannot convert access token to JSON"
  4. }

​ 到这里,本篇就结束了,我们总结一下:

  1. 使用基本的token,需要去认证中心验证token是否有效等,在高并发时会有性能问题
  2. 令牌改成JWT格式,解决了性能问题,当然也可以考虑将令牌存放在redis中也可以解决性能问题
  3. 本文栗子中使用对称加密的JWT令牌,生产环境肯定是使用非对称加密的密钥对(如何配置,童鞋们可以实践一下)

​ spring security相关的内容就写到这里,在spring security 及Oauth2的源码中发现了许多不明白的地方,之前spring的源码由粗略看到一部分,现在觉得还是需要再回头看看spring源码,所以后续会分享spring源码的章节。

spring security oauth2搭建resource-server demo及token改造成JWT令牌的更多相关文章

  1. spring security oauth2 搭建认证中心demo

    oauth2 介绍 ​ oauth2 协议应该是开发者们耳熟能详的协议了,这里就不做过多的介绍了,具体介绍如何在spring security中搭建oauth2的认证服务.Spring-Securit ...

  2. 基于Spring Security OAuth2搭建的Spring Cloud 认证中心

    Github传送门:https://github.com/13babybear/bounter-springcloud 实现功能有: 整合JWT 刷新Token 自定义客户端储存 自定义用户储存 资源 ...

  3. 关于 Spring Security OAuth2 中 CORS 跨域问题

    CORS 是一个 W3C 标准,全称是”跨域资源共享”(Cross-origin resource sharing).它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了 AJA ...

  4. springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式)

    项目security_simple(认证授权项目) 1.新建springboot项目 这儿选择springboot版本我选择的是2.0.6 点击finish后完成项目的创建 2.引入maven依赖  ...

  5. Spring security oauth2最简单入门环境搭建

    关于OAuth2的一些简介,见我的上篇blog:http://wwwcomy.iteye.com/blog/2229889 PS:貌似内容太水直接被鹳狸猿干沉.. 友情提示 学习曲线:spring+s ...

  6. Spring Security OAuth2 Demo —— 授权码模式

    本文可以转载,但请注明出处https://www.cnblogs.com/hellxz/p/oauth2_oauthcode_pattern.html 写在前边 在文章OAuth 2.0 概念及授权流 ...

  7. Spring Security OAuth2 Demo —— 密码模式(Password)

    前情回顾 前几节分享了OAuth2的流程与授权码模式和隐式授权模式两种的Demo,我们了解到授权码模式是OAuth2四种模式流程最复杂模式,复杂程度由大至小:授权码模式 > 隐式授权模式 > ...

  8. Spring Security OAuth2 Demo —— 客户端模式(ClientCredentials)

    前情回顾 前几节分享了OAuth2的流程与其它三种授权模式,这几种授权模式复杂程度由大至小:授权码模式 > 隐式授权模式 > 密码模式 > 客户端模式 本文要讲的是最后一种也是最简单 ...

  9. Spring Security OAuth2.0认证授权二:搭建资源服务

    在上一篇文章[Spring Security OAuth2.0认证授权一:框架搭建和认证测试](https://www.cnblogs.com/kuangdaoyizhimei/p/14250374. ...

随机推荐

  1. [loj2135]幻想乡战略游戏

    以1为根建树,令$D_{i}$为$i$子树内所有节点$d_{i}$之和 令$ans_{i}$为节点$i$的答案,令$fa$为$i$的父亲,则$ans_{i}=ans_{fa}+dis(i,fa)(D_ ...

  2. 【Vue.js】SPA

    SPA 2019-11-13  23:20:48  by冲冲 1.概念 (1)MPA(multi-page application) 特点:每一次页面跳转的时候,后台服务器都会返回一个新的html文档 ...

  3. pyinstaller进行打包exe文件

    百度直接pip安装,报错 下载离线文件报错. 百度了一下:还真好使 Python生成可执行文件主要有三种方法,利用py2exe,pyInstaller或cx_Freeze. 这里选择pyinstall ...

  4. volatile不能保证数据完整性的小案例

    package juc; import java.util.Collections; import java.util.HashSet; import java.util.Set; public cl ...

  5. 整理记录一些好用的随机图API

    最近自己博客使用的随机图API有些不稳定,自己又去搜集了一些有意思的随机图API,这里做一个整理记录 注意!!!本文链接最后测试时间----2021年11月21日 主题作者Tagaki的API(有时候 ...

  6. CF1036F

    考虑这种一堆数字\(gcd = k\) 有经典做法. 考虑设\(f(x)\)为\(gcd\)是\(x\)的倍数的方案数. \(g(x)\)为\(gcd\)刚好为\(x\)的方案数. 则有 \(f(x) ...

  7. Codeforces 536D - Tavas in Kansas(dp)

    Codeforces 题目传送门 & 洛谷题目传送门 其实这题本该 2019 年 12 月就 AC 的(详情请见 ycx 发此题题解的时间),然鹅鸽到了现在-- 首先以 \(s,t\) 分别为 ...

  8. C语言 fastq文件转换为fasta文件

    目前只能处理短序列,若要处理长序列,可按照https://www.cnblogs.com/mmtinfo/p/13036039.html的读取方法. 1 #include <stdio.h> ...

  9. Dango之form校验组件

    目录 1.引入案例 2. form组件的功能 3. form组件的使用 3.1 自定义form校验类 3.2 校验数据 3.3 渲染页面 3.4 展示错误信息 3.5 自定义校验结果 3.6 form ...

  10. Linux实现批量添加用户及随机密码小脚本

    通过chpasswd命令可实现迅速为用户批量设置密码     实例:写一个脚本,实现批量添加20个用户user1-20,密码为用户名和后面跟5个随机字符 #!/bin/sh # 思路:通过for循环, ...