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

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

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

交互模型

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

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

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

第一步获取临时令牌

 //
// GET: /API2/UserAuth/
/// <summary>
/// 获取临时令牌
/// </summary>
/// <param name="source">请求来源</param>
/// <returns>
/// tem_token:xxxxx,//临时令牌(有效时间30分钟)
/// </returns>
public ActionResult GetTemToken(string source)
{
if (source.Equals("XX来源"))
{
var tem_token = "jxbtem_" + Tools.GetRandomString();//获取十一位唯一码
//改用Session保存
System.Web.HttpContext.Current.Application[tem_token] = source;
//写入有效期
System.Web.HttpContext.Current.Application["temtokenLimit"] = DateTime.Now.AddMinutes();
return Tools.GetResult(new { tem_token });
}
else
{
return Tools.GetResult("未知请求来源", null);
}
}

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

 /// <summary>
/// 使用临时令牌用户授权
/// </summary>
/// <param name="callbackurl">回调地址</param>
/// <param name="temtoken">临时令牌</param>
/// <returns>
/// 验证通过跳转用户授权,验证失败显示失败原因
/// </returns>
public ActionResult UserAuthorization(string callbackurl, string temtoken)
{
//校验参数合法性
if (string.IsNullOrEmpty(callbackurl) || string.IsNullOrEmpty(temtoken))
{
return Tools.GetResult("非法请求");
}
if (callbackurl.Trim().Substring(, ).ToLower().IndexOf("http") == -)
{
return Tools.GetResult("回调地址不合法");
}
//传递重要参数
object session = System.Web.HttpContext.Current.Application[temtoken];
object temtokenLimit = System.Web.HttpContext.Current.Application["temtokenLimit"];
if (session != null && temtokenLimit != null)
{
Session["callbackurl"] = callbackurl;
Session["temtokenLimit"] = temtokenLimit;
Session["temtoken"] = temtoken;
Session["source"] = session;
System.Web.HttpContext.Current.Application.Remove(temtoken);
System.Web.HttpContext.Current.Application.Remove("temtokenLimit");
return RedirectToAction("GotoUserAut");
}
else
{
System.Web.HttpContext.Current.Application.Remove(temtoken);
System.Web.HttpContext.Current.Application.Remove("temtokenLimit");
return Tools.GetResult("令牌验证失败");
}
}

第三步跳转用户授权页面

 /// <summary>
/// 跳转用户授权页面
/// </summary>
/// <returns></returns>
public ActionResult GotoUserAut(string MSG)
{
//验证授权信息
if (!string.IsNullOrEmpty(MSG))
{
ViewBag.MSG = MSG;
return View();
}
//获取用户信息
string uname = Request["uname"];
string pwd =Request["pwd"];
string md5 = Request["md5"];
ViewBag.MSG = "";
var source = Session["source"];
ViewBag.Source = source;
if (string.IsNullOrEmpty(uname) || string.IsNullOrEmpty(pwd))
{
ViewBag.MSG = "请输入用户名密码";
return View();
}
if (string.IsNullOrEmpty(md5) || md5.Equals(""))
{
pwd = ToolKit.EncryptMd5(pwd);
}
//验证用户信息
if (!string.IsNullOrEmpty(uname) && !string.IsNullOrEmpty(pwd))
{
var basma = DBHelper.BASMA.FirstOrDefault(ma => ma.MA001.Equals(uname) && ma.MA002.Equals(pwd));
if (basma != null)
{
return View(basma);
}
else
{
ViewBag.MSG = "用户名或密码错误";
return View();
}
}
return View();
}

第四步确认授权登录

 /// <summary>
/// 确认授权登录
/// </summary>
/// <param name="UserId">用户ID</param>
/// <returns>
/// 跳转第三方回调页面
/// </returns>
public ActionResult StartUserAuthorization(int UserId)
{
BASMA UserInfo = null;
if (UserExist(UserId, out UserInfo))
{
var source = Session["source"];
var callbackurl = Session["callbackurl"];
var temtoken = Session["temtoken"];
var temtokenLimit = Session["temtokenLimit"];
if (source==null|| callbackurl==null || temtoken==null || temtokenLimit==null)
{
return RedirectToAction("GotoUserAut", new { MSG = "授权信息验证失败" });
}
var accesstoken = "jxb_" + Tools.GetRandomString();//获取十四位唯一码
//验证是否已授权
var basau = DBHelper.BASAU.FirstOrDefault(au => au.AU001.Equals(UserId) && au.AU002.Equals(source.ToString()));
if (basau == null)
{
BASAU newbasau = new BASAU();
newbasau.AU001 = UserId;
newbasau.AU002 = source.ToString();
newbasau.AU003 = temtoken.ToString();
newbasau.AU004 = DateTime.Parse(temtokenLimit.ToString());
newbasau.AU005 = accesstoken;
newbasau.AU006 = DateTime.Now;
DBHelper.BASAU.InsertOnSubmit(newbasau);
DBHelper.SubmitChanges();
}
else
{
basau.AU003 = temtoken.ToString();
basau.AU004 = DateTime.Parse(temtokenLimit.ToString());
basau.AU005 = accesstoken;
basau.AU006 = DateTime.Now;
DBHelper.SubmitChanges();
} //计算回调返回code
TimeSpan ts = (basau.AU006??DateTime.Now) - new DateTime(, , , , , , );//计算时间戳
string TimeSpan = Convert.ToInt64(ts.TotalMilliseconds).ToString(); //获得时间戳 return Redirect(callbackurl + "?code=" + TimeSpan);//回调返回授权码
}
else
{
return RedirectToAction("GotoUserAut", new { MSG = "指定授权用户不存在" });
}
}

第五步获取应用授权令牌

 /// <summary>
/// 获取应用授权令牌
/// </summary>
/// <param name="code">授权成功返回码</param>
/// <param name="temtoken">请求临时令牌</param>
/// <returns>
/// accesstoken:xxxxx,//授权码
/// </returns>
public ActionResult GetAccessToken(string code, string temtoken)
{
if (string.IsNullOrEmpty(code) || string.IsNullOrEmpty(temtoken))
{
return Tools.GetResult("请求参数不能为空", null);
}
DateTime dtBase = new DateTime(, , , , , , DateTimeKind.Utc);
DateTime convertTime = dtBase.Add(new TimeSpan(long.Parse(code) * TimeSpan.TicksPerMillisecond));
var BASAU = DBHelper.BASAU.FirstOrDefault(au => au.AU006.Equals(convertTime) && au.AU003.Equals(temtoken)&&au.AU004.Value>=DateTime.Now);
if (BASAU != null)
{
return Tools.GetResult(new { accesstoken = BASAU.AU005 });
}
else
{
return Tools.GetResult("令牌验证失败", null);
}
}

第六步获取用户唯一标识

/// <summary>
/// 获取用户唯一标识
/// </summary>
/// <param name="accesstoken">授权令牌</param>
/// <returns>
/// MA099:xxxxx,//用户唯一标识
/// MA010:xxxxx,//用户昵称
/// </returns>
public ActionResult GetUserInfo(string accesstoken)
{
if (!string.IsNullOrEmpty(accesstoken))
{
var basau = DBHelper.BASAU.FirstOrDefault(ua => ua.AU005.Equals(accesstoken));
if (basau != null)
{
var basma = DBHelper.BASMA.FirstOrDefault(ma => ma.ID.Equals(basau.AU001));
if (basma != null)
{
return Tools.GetResult(new { basma.MA099, basma.MA010 });
}
else
{
return Tools.GetResult("用户信息拉取失败", null);
}
}
else
{
return Tools.GetResult("令牌验证失败", null);
}
}
else
{
return Tools.GetResult("请求参数非法",null);
}
}

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

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

@model Ecio_Admin.Models.BASMA
@{
ViewBag.Title = "GotoUserAut";
Layout = null;
//获取用户ID
var UserId = "";
if (Model != null)
{
UserId = Model.ID.ToString();
}
}
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<meta charset="UTF-8">
<title>应用授权</title>
<link href="~/Content/css/UserAut.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
$(function () {
//授权
$("#userauth").click(
function () {
location.href = "/API2/UserAuth/StartUserAuthorization?UserId=" + $("#UserId").val();
});
});
</script>
</head>
<body>
<div class="box">
<input type="hidden" id="UserId" value="@UserId" />
<img class="logo" src="~/UploadFiles/IMG/OAuth.png" alt="LOGO" />
@{
if (Model != null)
{
<h1 class="title">登录后该应用将获得以下授权:</h1>
<h2 class="title2"><input type="checkbox" checked readonly="readonly" disabled="disabled">将获取您的基本信息(昵称、头像)</h2>
<form class="form-group" action="#" method="post">
<input class="form-btn" style="cursor:pointer;" type="button" id="userauth" value="确定授权" />
</form>
}
else
{
<h1 class="title">授权<span>@ViewBag.Source</span>访问你的XXX账号</h1>
<form class="form-group" action="/API2/UserAuth/GotoUserAut" method="post">
<input class="form-import" type="text" placeholder="请输入您的账号" id="uname" name="uname" required autocomplete="off" />
<input class="form-import" type="password" placeholder="请输入您的密码" id="pwd" name="pwd" required autocomplete="off" />
<div style="color:red;">@ViewBag.MSG</div>
<input class="form-btn" style="cursor:pointer;" type="submit" id="Login" value="登录" />
</form>
}
}
</div>
</body>
</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. MyBatis #{} 取值注意事项

    正确写法#{key} 错误写法#{key } #{}中不能加空格,不然会报错

  2. Excel列名和列序号转换

    大家有没有留意过Excel表格中列名的规律呢?是这样的:A B C ... Y Z AA AB AC ... AY AZ BA BB BC ... BY BZ ... ZZ ... AAA ... 如 ...

  3. 《DSP using MATLAB》Problem 6.17

    代码: %% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ %% Output In ...

  4. hdu2174 kiki's game 博弈

    Recently kiki has nothing to do. While she is bored, an idea appears in his mind, she just playes th ...

  5. python------模块定义、导入、优化 ------->hashlib模块

    一.hashlib模块 用于加密相关的操作,3.x版本里代替了md5模块和sha模块,主要提供SHA1,SHA224,SHA256,SHA384,SHA512,MD5算法. (MD5消息摘要算法(英语 ...

  6. ★ MYSQL隔离级别 通俗理解 + mysql、oracle默认事务隔离级别

    ★  脏读 : 读取了前一事务 未提交 的数据 ; 不可重复读    : 读取了前一事务     提交 的数据: ★ 幻读 与 不可重复读 common :都是读取了另一条已经提交的事务(这点与脏读不 ...

  7. python可变类型和不可变类型

    原文地址:http://www.cnblogs.com/huamingao/p/5809936.html 可变类型 Vs 不可变类型 可变类型(mutable):列表,字典 不可变类型(unmutab ...

  8. 深入理解计算机系统 (Randal E.Bryant / David O'Hallaron 著)

    第1章 计算机系统漫游 (已看) 1.1 信息就是位+上下文 1.2 程序被其他程序翻译成不同的格式 1.3 了解编译系统如何工作是大有益处的 1.4 处理器读并解释存储在内存中的指令 1.4.1 系 ...

  9. promise对象的回调函数resolve的参数为另一个promise对象

    /*如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数. reject函数的参数通常是Error对象的实例,表示抛出的错误: resolve函数的参数除了正常的值 ...

  10. 关注 硬件 发展, 转载一篇介绍 VHDL 的文章

    <VHDL学习笔记> https://www.eefocus.com/hrbeulvcaho/blog/12-11/289109_978e2.html VHDL 和  “可编程逻辑阵列”  ...