下面先简单介绍一下Oauth2的原理

Oauth2是什么?

Oauth2是一种授权机制,用来授权给第三方应用,获取用户数据。

Oauth2是什么的解释 这是阮一峰老师的解释,因此解释的比较好了,就不在此重复了。

Oauth2 的原理

Oauth2 有四种授权模型 授权码,隐藏式,密码式,凭证式 目前主流的形式是授权码方式。 我们项目中使用的也是授权码方式。 这里就只介绍一下授权码方式 。

授权码方式

这块的内容完全是引用于Oauth2原理 只是为了方便大家截阅读不用在来回跳转。 其他的几种方式可以去这篇文章中查看。

授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。

这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。

第一步,A 网站提供一个链接,用户点击后就会跳转到 B 网站,授权用户数据给 A 网站使用。下面就是 A 网站跳转 B 网站的一个示意链接。

https://b.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read

上面 URL 中,response_type参数表示要求返回授权码(code),client_id参数让 B 知道是谁在请求,redirect_uri参数是 B 接受或拒绝请求后的跳转网址,scope参数表示要求的授权范围(这里是只读)。

第二步,用户跳转后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。用户表示同意,这时 B 网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码,就像下面这样。
https://a.com/callback?code=AUTHORIZATION_CODE
上面 URL 中,code参数就是授权码。

第三步,A 网站拿到授权码以后,就可以在后端,向 B 网站请求令牌。


https://b.com/oauth/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=CALLBACK_URL

上面 URL 中,client_id参数和client_secret参数用来让 B 确认 A 的身份(client_secret参数是保密的,因此只能在后端发请求),grant_type参数的值是AUTHORIZATION_CODE,表示采用的授权方式是授权码,code参数是上一步拿到的授权码,redirect_uri参数是令牌颁发后的回调网址。

第四步,B 网站收到请求以后,就会颁发令牌。具体做法是向redirect_uri指定的网址,发送一段 JSON 数据。

{    "access_token":"ACCESS_TOKEN","token_type":"bearer","expires_in":2592000,"refresh_token":"REFRESH_TOKEN","scope":"read","uid":100101,"info":{...}
}

上面 JSON 数据中,access_token字段就是令牌,A 网站在后端拿到了。

单点登录的实现

下面我们就要开始动手了。

第一步,配置哪些应用可以到我们服务器中来获取认证。

可以看下图里面的关键往上,clientId,redirectUri 就是我们在上面的原理介绍中包括的, 这里还有一个关键的信息 就是clientSecret 。 因此我们不能随便让人拿到clientId 就能来我们服务器中认证。 我们需要一点案例机制。就是生成一个密钥,告诉第三方的应用,让他请求授权的时候,把这个密钥带上,否则就是非法请求。 其他的参数只是为了管理方便使用。

第二步: 获取授权码

下面可以看看真实的请求路径

http://127.0.0.1:9999/tboot/oauth2/authorize?username=superman&password=123456&code=560p&client_id=1287990317873762304&redirect_uri=http:%2F%2F127.0.0.1:10001%2F&state=1234

下面看看java代码

  @RequestMapping(value = "/authorize", method = RequestMethod.GET)@ApiOperation(value = "认证获取code")public Result<Object> authorize(@ApiParam("用户名") @RequestParam String username,@ApiParam("密码") @RequestParam String password,@ApiParam("客户端id") @RequestParam String client_id,@ApiParam("成功授权后回调地址") @RequestParam String redirect_uri,@ApiParam("授权类型为code") @RequestParam(required = false, defaultValue = "code") String response_type,@ApiParam("客户端状态值") @RequestParam String state){Client client = clientService.getById(client_id);if(client==null){return ResultUtil.error("客户端client_id不存在");}User user = userService.findByUsername(username);if(user==null){return ResultUtil.error("用户名不存在");}if(!new BCryptPasswordEncoder().matches(password, user.getPassword())){return ResultUtil.error("用户密码不正确");}// 判断回调地址if(!client.getRedirectUri().equals(redirect_uri)){return ResultUtil.error("回调地址redirect_uri不正确");}// 生成code 5分钟内有效String code = UUID.randomUUID().toString().replace("-", "");// 存入用户及clientId信息redisTemplate.opsForValue().set("oauthCode:"+code, new Gson().toJson(new Oauth2TokenInfo(client_id, username)), 5L, TimeUnit.MINUTES);Map<String, Object> map = new HashMap<>(16);map.put("code", code);map.put("redirect_uri", redirect_uri);map.put("state", state);return ResultUtil.data(map);}

第三步: 获取token

这样就返回了授权码,然后夺通过 授权码获取accessToken就算完成了。

查看请求路径

http://127.0.0.1:10001/tboot/oauth2/token?code=3838482b50b1447e81cbf9e52403f629&response_type=code&grant_type=authorization_code&client_id=1287990317873762304&client_secret=f3ede985786a40c2b0d7c341c83af4f3&redirect_uri=http:%2F%2F127.0.0.1:10001%2F

查看java代码

 @RequestMapping(value = "/token", method = RequestMethod.GET)@ApiOperation(value = "获取accessToken令牌")public Result<Object> token(@ApiParam("授权类型") @RequestParam String grant_type,@ApiParam("客户端id") @RequestParam String client_id,@ApiParam("客户端秘钥") @RequestParam String client_secret,@ApiParam("认证返回的code") @RequestParam(required = false) String code,@ApiParam("刷新token") @RequestParam(required = false) String refresh_token,@ApiParam("成功授权后回调地址") @RequestParam(required = false) String redirect_uri){Client client = clientService.getById(client_id);if(client==null){return ResultUtil.error("客户端client_id不存在");}// 判断clientSecretif(!client.getClientSecret().equals(client_secret)){return ResultUtil.error("client_secret不正确");}Oauth2TokenInfo tokenInfo = null;if("authorization_code".equals(grant_type)){// 判断回调地址if(!client.getRedirectUri().equals(redirect_uri)){return ResultUtil.error("回调地址redirect_uri不正确");}// 判断code 获取用户信息String codeValue = redisTemplate.opsForValue().get("oauthCode:"+code);if(StrUtil.isBlank(codeValue)){return ResultUtil.error("code已过期");}tokenInfo = new Gson().fromJson(codeValue, Oauth2TokenInfo.class);if(!tokenInfo.getClientId().equals(client_id)){return ResultUtil.error("code不正确");}} else if ("refresh_token".equals(grant_type)){// 从refreshToken中获取用户信息String refreshTokenValue = redisTemplate.opsForValue().get("oauthTokenInfo:"+refresh_token);if(StrUtil.isBlank(refreshTokenValue)){return ResultUtil.error("refresh_token已过期");}tokenInfo = new Gson().fromJson(refreshTokenValue, Oauth2TokenInfo.class);if(!tokenInfo.getClientId().equals(client_id)){return ResultUtil.error("refresh_token不正确");}} else {return ResultUtil.error("授权类型grant_type不正确");}String token = null, refreshToken = null;Long expiresIn = null;String tokenKey = "oauthToken:"+client_id+":"+tokenInfo.getUsername(), refreshKey = "oauthRefreshToken:"+client_id+":"+tokenInfo.getUsername();if("authorization_code".equals(grant_type)){// 生成token模式String oldToken = redisTemplate.opsForValue().get(tokenKey);String oldRefreshToken = redisTemplate.opsForValue().get(refreshKey);if(StrUtil.isNotBlank(oldToken)&&StrUtil.isNotBlank(oldRefreshToken)){// 旧tokentoken = oldToken;refreshToken = oldRefreshToken;expiresIn = redisTemplate.getExpire("oauthTokenInfo:"+token, TimeUnit.SECONDS);} else {// 新生成 30天过期String newToken = UUID.randomUUID().toString().replace("-", "");String newRefreshToken = UUID.randomUUID().toString().replace("-", "");redisTemplate.opsForValue().set(tokenKey, newToken, 30L, TimeUnit.DAYS);redisTemplate.opsForValue().set(refreshKey, newRefreshToken, 30L, TimeUnit.DAYS);// 新token中存入用户信息redisTemplate.opsForValue().set("oauthTokenInfo:"+newToken, new Gson().toJson(tokenInfo),30L, TimeUnit.DAYS);redisTemplate.opsForValue().set("oauthTokenInfo:"+newRefreshToken, new Gson().toJson(tokenInfo),30L, TimeUnit.DAYS);token = newToken;refreshToken = newRefreshToken;expiresIn = redisTemplate.getExpire(token, TimeUnit.SECONDS);}} else if("refresh_token".equals(grant_type)) {// 刷新token模式 生成新token 30天过期String newToken = UUID.randomUUID().toString().replace("-", "");String newRefreshToken = UUID.randomUUID().toString().replace("-", "");redisTemplate.opsForValue().set(tokenKey, newToken, 30L, TimeUnit.DAYS);redisTemplate.opsForValue().set(refreshKey, newRefreshToken, 30L, TimeUnit.DAYS);// 新token中存入用户信息redisTemplate.opsForValue().set("oauthTokenInfo:"+newToken, new Gson().toJson(tokenInfo),30L, TimeUnit.DAYS);redisTemplate.opsForValue().set("oauthTokenInfo:"+newRefreshToken, new Gson().toJson(tokenInfo),30L, TimeUnit.DAYS);token = newToken;refreshToken = newRefreshToken;expiresIn = redisTemplate.getExpire("oauthTokenInfo:"+token, TimeUnit.SECONDS);// 旧refreshToken过期redisTemplate.delete("oauthTokenInfo:"+refresh_token);}Map<String, Object> map = new HashMap<>(16);map.put("access_token", token);map.put("expires_in", expiresIn);map.put("refresh_token", refreshToken);return ResultUtil.data(map);}

就可以获取相当的信息
在通过 accessTonken 就可去资源服务器去获取相应的用户信息了。

重要

猿来衣舍

这是博主开的淘宝小店,主要经营舒适保暖的服饰,**有纯棉防臭袜子、主题卫衣、恒温发热保暖衣**。欢迎大家选购。一个人能够走多远,关键在于与谁同行,我用跨越山海的一路相伴,希望得到您用金钱的称赞。
猿来衣舍
猿来衣舍
猿来衣舍
猿来衣舍

打开淘宝搜索 “猿来衣舍”这四个字就可以搜到小店,希望大家多多支持。

交个朋友吧

Oauth2方式实现单点登录相关推荐

  1. 使用Spring Secuirty Oauth2实现SSO单点登录

    文章目录 1. 什么是单点登录 2. 微服务架构下单点登录的思路 3. 使用 Spring Secuirty Oauth2 实现SSO单点登录 ①:建表 ②:授权服务器逻辑 ③:网关逻辑 4. 接口测 ...

  2. Oauth2.0实现单点登录的原理流程,这次总该懂了!

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

  3. Oauth2.0实现单点登录的原理流程,通俗易懂

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

  4. Oauth2.0实现单点登录的原理流程,通俗易懂!

    点击上方☝码猿技术专栏 轻松关注,设为星标! 及时获取有趣有料的技术 单点登录是多域名企业站点流行的登录方式.本文以现实生活场景辅助理解,力争彻底理清 OAuth2.0 实现单点登录的原理流程.同时总 ...

  5. 基于Spring Security + OAuth2 的SSO单点登录(服务端)

    相关技术 spring security: 用于安全控制的权限框架 OAuth2: 用于第三方登录认证授权的协议 JWT:客户端和服务端通信的数据载体 传统登录 登录web系统后将用户信息保存在ses ...

  6. 前后端分离基于Oauth2的SSO单点登录怎样做?

    一.说明 单点登录顾名思义就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,免除多次登录的烦恼:本文主要介绍跨域间的 前后端分离 项目怎样实现单点登录,并且与 非前后端分离 的差 ...

  7. Oauth2.0实现单点登录的原理流程

    Oauth2.0实现单点登录的原理流程 1.什么是单点登录 2.OAuth2 认证授权的原理流程 3.基于 SpringBoot 实现认证/授权 4.综合运用 1.什么是单点登录 1.1 多点登录 传 ...

  8. java oauth sso 源码_基于Spring Security Oauth2的SSO单点登录+JWT权限控制实践

    概 述 在前文<基于Spring Security和 JWT的权限系统设计>之中已经讨论过基于 Spring Security和 JWT的权限系统用法和实践,本文则进一步实践一下基于 Sp ...

  9. SpringBoot+OAuth2+JWT实现单点登录SSO完整教程,竟如此简单优雅!

    作者:狂乱的贵公子 来源:https://www.cnblogs.com/cjsblog/p/10548022.html 1.前言 技术这东西吧,看别人写的好像很简单似的,到自己去写的时候就各种问题, ...

最新文章

  1. java微信菜单获取openid_微信二次开发点击菜单openId的获取
  2. 使用函数求两个整数的最大公约数和最小公倍数
  3. windows mobile 5.0 PocketPC模拟器上网的设置 【正确】
  4. Redis集群读写分离架构搭建以及主从数据连通验证(附加集群口令认证以及Redis端口6379释放)
  5. 树莓派wiringPi库详解
  6. Redis添加主节点
  7. 20165211 2017-2018-2 《Java程序设计》第4周学习总结
  8. c语言打印数组元素_C程序打印元素差为0或1的子集数
  9. html提交表单给php邮件发送,在HTML表单中通过PHP自动发送电子邮件
  10. 《Java并发性和多线程介绍》-Java TheadLocal
  11. python读yaml的库_Python读取YAML文件过程详解
  12. React全家桶项目
  13. 4月18日会议总结(整理—祁子梁)
  14. 计算机英语词典 txt,9种Txt格式朗文英语词典免费分享
  15. 给初学日语者的几点建议——词汇篇
  16. 服务器ie不能打开购物网站,[Answers 分享]通过IE浏览器无法打开网上银行或者支付宝等加密安全站点...
  17. ArcGIS10从入门到精通系列实验图文教程(附配套实验数据持续更新)
  18. 测试用例编写练习(二)
  19. 模糊视频帧插值:CVPR2020论文点评
  20. 上海电机学院计算机科学与技术专业怎么样,上海电机学院计算机科学与技术专业2016年在上海理科高考录取最低分数线...

热门文章

  1. 电路基本原理那些事儿之 欧姆定律
  2. 实用计算机技术选修,上海音乐学院附中2013学年选修课程:计算机技术
  3. 双百双新产业项目是什么_广西28个“双百双新”产业项目集中开竣工
  4. 如何制作mac下的icns图标
  5. Ext.form.ComboBox 属性详解及使用方法介绍和级联使用
  6. Mgc token十问(上)
  7. Chukwa搭建、安装、部署、应用
  8. c++解鸡兔同笼(3)
  9. 五分钟包教你学会写Shell脚本
  10. mac版本cornerstone的无限期破解方法