今天看到了博友对SSO的文章,SSO单点登录的讲解突然想写一篇关于OAuth2.0用户授权的介绍。

应用场景:在APP或者网页接入一些第三方应用时,时长会需要用户登录另一个合作平台,比如QQ,微博,微信的授权登录。

使用好处:这样可以免去用户同步的麻烦,同时也增加了用户信息的安全。

交互模型

1.接口需要经过“1 次认证+1 次授权+1 次审核” 即可获得 accesstoken,请求业 务模式说明:
1.1 来源认证: 用户访问您的移动应用(网页),请根据确认合作名称(例如: 金融网)作为 来源认证,服务端验证无误后会返回一个临时令牌( 有效期 30 分钟)。开发授权的话这一步就可以忽略来源认证
1.2 用户授权: 接收到临时令牌加入本次请求回调地址( 回调地址格式: https:// 或 http://外网可访问网址), 用户登录授权通过后将会在回调地址中返回授权通 过码( 参数名: code)。

1.3 令牌审核: 第一步访问得到临时令牌,第二次请求得到授权通过码, 授权通过码加临时令牌通过后将会返回 AccessToken( 注意临时令牌有效时间 30 分钟)。

临时令牌的时间可以根据实际情况设置

第一步获取临时令牌

  1. //
  2. // GET: /API2/UserAuth/
  3. /// <summary>
  4. /// 获取临时令牌
  5. /// </summary>
  6. /// <param name="source">请求来源</param>
  7. /// <returns>
  8. /// tem_token:xxxxx,//临时令牌(有效时间30分钟)
  9. /// </returns>
  10. public ActionResult GetTemToken(string source)
  11. {
  12. if (source.Equals("XX来源"))
  13. {
  14. var tem_token = "jxbtem_" + Tools.GetRandomString();//获取十一位唯一码
  15. //改用Session保存
  16. System.Web.HttpContext.Current.Application[tem_token] = source;
  17. //写入有效期
  18. System.Web.HttpContext.Current.Application["temtokenLimit"] = DateTime.Now.AddMinutes();
  19. return Tools.GetResult(new { tem_token });
  20. }
  21. else
  22. {
  23. return Tools.GetResult("未知请求来源", null);
  24. }
  25. }

第二步使用临时令牌用户授权

  1. /// <summary>
  2. /// 使用临时令牌用户授权
  3. /// </summary>
  4. /// <param name="callbackurl">回调地址</param>
  5. /// <param name="temtoken">临时令牌</param>
  6. /// <returns>
  7. /// 验证通过跳转用户授权,验证失败显示失败原因
  8. /// </returns>
  9. public ActionResult UserAuthorization(string callbackurl, string temtoken)
  10. {
  11. //校验参数合法性
  12. if (string.IsNullOrEmpty(callbackurl) || string.IsNullOrEmpty(temtoken))
  13. {
  14. return Tools.GetResult("非法请求");
  15. }
  16. if (callbackurl.Trim().Substring(, ).ToLower().IndexOf("http") == -)
  17. {
  18. return Tools.GetResult("回调地址不合法");
  19. }
  20. //传递重要参数
  21. object session = System.Web.HttpContext.Current.Application[temtoken];
  22. object temtokenLimit = System.Web.HttpContext.Current.Application["temtokenLimit"];
  23. if (session != null && temtokenLimit != null)
  24. {
  25. Session["callbackurl"] = callbackurl;
  26. Session["temtokenLimit"] = temtokenLimit;
  27. Session["temtoken"] = temtoken;
  28. Session["source"] = session;
  29. System.Web.HttpContext.Current.Application.Remove(temtoken);
  30. System.Web.HttpContext.Current.Application.Remove("temtokenLimit");
  31. return RedirectToAction("GotoUserAut");
  32. }
  33. else
  34. {
  35. System.Web.HttpContext.Current.Application.Remove(temtoken);
  36. System.Web.HttpContext.Current.Application.Remove("temtokenLimit");
  37. return Tools.GetResult("令牌验证失败");
  38. }
  39. }

第三步跳转用户授权页面

  1. /// <summary>
  2. /// 跳转用户授权页面
  3. /// </summary>
  4. /// <returns></returns>
  5. public ActionResult GotoUserAut(string MSG)
  6. {
  7. //验证授权信息
  8. if (!string.IsNullOrEmpty(MSG))
  9. {
  10. ViewBag.MSG = MSG;
  11. return View();
  12. }
  13. //获取用户信息
  14. string uname = Request["uname"];
  15. string pwd =Request["pwd"];
  16. string md5 = Request["md5"];
  17. ViewBag.MSG = "";
  18. var source = Session["source"];
  19. ViewBag.Source = source;
  20. if (string.IsNullOrEmpty(uname) || string.IsNullOrEmpty(pwd))
  21. {
  22. ViewBag.MSG = "请输入用户名密码";
  23. return View();
  24. }
  25. if (string.IsNullOrEmpty(md5) || md5.Equals(""))
  26. {
  27. pwd = ToolKit.EncryptMd5(pwd);
  28. }
  29. //验证用户信息
  30. if (!string.IsNullOrEmpty(uname) && !string.IsNullOrEmpty(pwd))
  31. {
  32. var basma = DBHelper.BASMA.FirstOrDefault(ma => ma.MA001.Equals(uname) && ma.MA002.Equals(pwd));
  33. if (basma != null)
  34. {
  35. return View(basma);
  36. }
  37. else
  38. {
  39. ViewBag.MSG = "用户名或密码错误";
  40. return View();
  41. }
  42. }
  43. return View();
  44. }

第四步确认授权登录

  1. /// <summary>
  2. /// 确认授权登录
  3. /// </summary>
  4. /// <param name="UserId">用户ID</param>
  5. /// <returns>
  6. /// 跳转第三方回调页面
  7. /// </returns>
  8. public ActionResult StartUserAuthorization(int UserId)
  9. {
  10. BASMA UserInfo = null;
  11. if (UserExist(UserId, out UserInfo))
  12. {
  13. var source = Session["source"];
  14. var callbackurl = Session["callbackurl"];
  15. var temtoken = Session["temtoken"];
  16. var temtokenLimit = Session["temtokenLimit"];
  17. if (source==null|| callbackurl==null || temtoken==null || temtokenLimit==null)
  18. {
  19. return RedirectToAction("GotoUserAut", new { MSG = "授权信息验证失败" });
  20. }
  21. var accesstoken = "jxb_" + Tools.GetRandomString();//获取十四位唯一码
  22. //验证是否已授权
  23. var basau = DBHelper.BASAU.FirstOrDefault(au => au.AU001.Equals(UserId) && au.AU002.Equals(source.ToString()));
  24. if (basau == null)
  25. {
  26. BASAU newbasau = new BASAU();
  27. newbasau.AU001 = UserId;
  28. newbasau.AU002 = source.ToString();
  29. newbasau.AU003 = temtoken.ToString();
  30. newbasau.AU004 = DateTime.Parse(temtokenLimit.ToString());
  31. newbasau.AU005 = accesstoken;
  32. newbasau.AU006 = DateTime.Now;
  33. DBHelper.BASAU.InsertOnSubmit(newbasau);
  34. DBHelper.SubmitChanges();
  35. }
  36. else
  37. {
  38. basau.AU003 = temtoken.ToString();
  39. basau.AU004 = DateTime.Parse(temtokenLimit.ToString());
  40. basau.AU005 = accesstoken;
  41. basau.AU006 = DateTime.Now;
  42. DBHelper.SubmitChanges();
  43. }
  44.  
  45. //计算回调返回code
  46. TimeSpan ts = (basau.AU006??DateTime.Now) - new DateTime(, , , , , , );//计算时间戳
  47. string TimeSpan = Convert.ToInt64(ts.TotalMilliseconds).ToString(); //获得时间戳
  48.  
  49. return Redirect(callbackurl + "?code=" + TimeSpan);//回调返回授权码
  50. }
  51. else
  52. {
  53. return RedirectToAction("GotoUserAut", new { MSG = "指定授权用户不存在" });
  54. }
  55. }

第五步获取应用授权令牌

  1. /// <summary>
  2. /// 获取应用授权令牌
  3. /// </summary>
  4. /// <param name="code">授权成功返回码</param>
  5. /// <param name="temtoken">请求临时令牌</param>
  6. /// <returns>
  7. /// accesstoken:xxxxx,//授权码
  8. /// </returns>
  9. public ActionResult GetAccessToken(string code, string temtoken)
  10. {
  11. if (string.IsNullOrEmpty(code) || string.IsNullOrEmpty(temtoken))
  12. {
  13. return Tools.GetResult("请求参数不能为空", null);
  14. }
  15. DateTime dtBase = new DateTime(, , , , , , DateTimeKind.Utc);
  16. DateTime convertTime = dtBase.Add(new TimeSpan(long.Parse(code) * TimeSpan.TicksPerMillisecond));
  17. var BASAU = DBHelper.BASAU.FirstOrDefault(au => au.AU006.Equals(convertTime) && au.AU003.Equals(temtoken)&&au.AU004.Value>=DateTime.Now);
  18. if (BASAU != null)
  19. {
  20. return Tools.GetResult(new { accesstoken = BASAU.AU005 });
  21. }
  22. else
  23. {
  24. return Tools.GetResult("令牌验证失败", null);
  25. }
  26. }

第六步获取用户唯一标识

  1. /// <summary>
  2. /// 获取用户唯一标识
  3. /// </summary>
  4. /// <param name="accesstoken">授权令牌</param>
  5. /// <returns>
  6. /// MA099:xxxxx,//用户唯一标识
  7. /// MA010:xxxxx,//用户昵称
  8. /// </returns>
  9. public ActionResult GetUserInfo(string accesstoken)
  10. {
  11. if (!string.IsNullOrEmpty(accesstoken))
  12. {
  13. var basau = DBHelper.BASAU.FirstOrDefault(ua => ua.AU005.Equals(accesstoken));
  14. if (basau != null)
  15. {
  16. var basma = DBHelper.BASMA.FirstOrDefault(ma => ma.ID.Equals(basau.AU001));
  17. if (basma != null)
  18. {
  19. return Tools.GetResult(new { basma.MA099, basma.MA010 });
  20. }
  21. else
  22. {
  23. return Tools.GetResult("用户信息拉取失败", null);
  24. }
  25. }
  26. else
  27. {
  28. return Tools.GetResult("令牌验证失败", null);
  29. }
  30. }
  31. else
  32. {
  33. return Tools.GetResult("请求参数非法",null);
  34. }
  35. }

授权是需要用户登录才能授权,如果在自己的应用内或者可以提供用户标识就可以直接通过。

附上完整的授权页面代码:

  1. @model Ecio_Admin.Models.BASMA
  2. @{
  3. ViewBag.Title = "GotoUserAut";
  4. Layout = null;
  5. //获取用户ID
  6. var UserId = "";
  7. if (Model != null)
  8. {
  9. UserId = Model.ID.ToString();
  10. }
  11. }
  12. <html>
  13. <head>
  14. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  15. <meta charset="UTF-8">
  16. <title>应用授权</title>
  17. <link href="~/Content/css/UserAut.css" rel="stylesheet" />
  18. <script src="~/Scripts/jquery-1.10.2.min.js"></script>
  19. <script type="text/javascript">
  20. $(function () {
  21. //授权
  22. $("#userauth").click(
  23. function () {
  24. location.href = "/API2/UserAuth/StartUserAuthorization?UserId=" + $("#UserId").val();
  25. });
  26. });
  27. </script>
  28. </head>
  29. <body>
  30. <div class="box">
  31. <input type="hidden" id="UserId" value="@UserId" />
  32. <img class="logo" src="~/UploadFiles/IMG/OAuth.png" alt="LOGO" />
  33. @{
  34. if (Model != null)
  35. {
  36. <h1 class="title">登录后该应用将获得以下授权:</h1>
  37. <h2 class="title2"><input type="checkbox" checked readonly="readonly" disabled="disabled">将获取您的基本信息(昵称、头像)</h2>
  38. <form class="form-group" action="#" method="post">
  39. <input class="form-btn" style="cursor:pointer;" type="button" id="userauth" value="确定授权" />
  40. </form>
  41. }
  42. else
  43. {
  44. <h1 class="title">授权<span>@ViewBag.Source</span>访问你的XXX账号</h1>
  45. <form class="form-group" action="/API2/UserAuth/GotoUserAut" method="post">
  46. <input class="form-import" type="text" placeholder="请输入您的账号" id="uname" name="uname" required autocomplete="off" />
  47. <input class="form-import" type="password" placeholder="请输入您的密码" id="pwd" name="pwd" required autocomplete="off" />
  48. <div style="color:red;">@ViewBag.MSG</div>
  49. <input class="form-btn" style="cursor:pointer;" type="submit" id="Login" value="登录" />
  50. </form>
  51. }
  52. }
  53. </div>
  54. </body>
  55. </html>

代码其实都是多余的,在编写时可以按照这种安全机制,去书写自己的授权逻辑。

一篇对OAuth2.0开发实例的介绍的更多相关文章

  1. OAuth2.0开发指南

    OAuth2.0开发指南 1.认证与登录 来往开放平台支持3种不同的OAuth 2.0验证与授权流程: 服务端流程(协议中Authorization Code Flow): 此流程适用于在Web服务端 ...

  2. OAuth2.0学习(2-1)Spring Security OAuth2.0 开发指南

    开发指南:http://www.cnblogs.com/xingxueliao/p/5911292.html Spring OAuth2.0 提供者实现原理: Spring OAuth2.0提供者实际 ...

  3. SignalR2.0开发实例之——设置时间、后台其他地方使用集线器、使用自己的连接ID

    一.连接的生命周期设置: 如下: // 该值表示连接在超时之前保持打开状态的时间长度. //默认为110秒 GlobalHost.Configuration.ConnectionTimeout = T ...

  4. SignalR2.0开发实例之——负载均衡

    SignalR 2.0作为一个新的而且强大的通信工具,发布博客之后得到了很多人的支持,谢谢...也有人对性能和架设等问题提出了各种质疑..真的很感谢.. 我特意下载了SignalR 2.0的源码硬着头 ...

  5. SignalR2.0开发实例之——私聊

    一.前言 继续上一章的补充,这章介绍使用私聊的功能.主要通过一个方法   Clients.Client(Context.ConnectionId).showMessage(msg); SignalR框 ...

  6. SignalR2.0开发实例之——群发消息

    一.前言 ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端可以互相 ...

  7. SignalR2.0开发实例之——创建房间聊天

    SignalR作为一个强大的集线器,已经在hub里面集成了Gorups,也就是分组管理,使用方法如下: //作用:将连接ID加入某个组 //Context.ConnectionId 连接ID,每个页面 ...

  8. RDIFramework.NET开发实例━表约束条件权限的使用-Web

    RDIFramework.NET开发实例━表约束条件权限的使用-Web 在上一篇文章“RDIFramework.NET开发实例━表约束条件权限的使用-WinForm”我们讲解了在WinForm下表约束 ...

  9. C#软件开发实例.私人订制自己的屏幕截图工具(七)加入放大镜的功能

    上一篇:C#软件开发实例.私人订制自己的屏幕截图工具(六)加入配置管理功能 因为截图时可能须要精确截取某一部分,所以须要放大镜的功能,这样截取的时候才更easy定位截图的位置. 加入PictureBo ...

随机推荐

  1. C++学习(二十六)(C语言部分)之 结构体3(联合,枚举)

    结构体 struct 类型定义点运算符 . 变量名.成员 成员是数组的时候不能用等于号赋值箭头运算符 -> 指针->成员 作用 存放多个不同类型的有关联的数据 与结构体类似的类型1.联合作 ...

  2. djkstra nlogn

    #include<bits/stdc++.h> #define fi first #define se second #define pii pair<int,int> usi ...

  3. Go Example--通道选择器

    package main import ( "fmt" "time" ) func main() { c1 := make(chan string) c2 := ...

  4. Go Example--通道方向

    package main import "fmt" func main() { pings := make(chan string, 1) pongs := make(chan s ...

  5. centos7安装部署mysql5.7服务器

    因为自带源没有最新版的mysql,所以我们需要自己下载rpm包,先下载下面的rpm包源 https://repo.mysql.com//mysql57-community-release-el7-11 ...

  6. Centos7快速安装haproxy

    HAProxy是一个使用C语言编写的自由及开放源代码软件[1],其提供高可用性.负载均衡,以及基于TCP和HTTP的应用程序代理. HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要 ...

  7. <--------------------------StringBuffer的常用方法------------------------------>

    StringBuffer定义: 是可变字符数组,是线程安全的可变字符序列. StringBuffer和String的区别: String是一个不可变的字符序列. 实例: public class St ...

  8. Scalable MySQL Cluster with Master-Slave Replication, ProxySQL Load Balancing and Orchestrator

    MySQL is one of the most popular open-source relational databases, used by lots of projects around t ...

  9. ipfs cluster 模式部署使用(docker-compose 环境运行)

    ipfs 点对点的分布式文件系统,官方提供了集群模式运行的docker 镜像,以及docker-compose 文件 所以测试下 环境准备 docker-compose   version: '3.4 ...

  10. jquery trigger函数和triggerHandler函数的对照

    一句话的差别就是:trigger will bubbling jQuery events (not default DOM events) and triggerHnadler will not do ...