Web Api跨域登录问题
最近项目第一次尝试使用web api,照搬了一般mvc的Forms登录方式,在和前端对接的时候出现一个问题:
前端使用ajax调用登录接口完成登录后,再调用别的接口,被判断为未登录。
如果直接在浏览器中先后访问登录接口和别的接口,则能识别为已登录。
对于asp.net的这些机制其实我了解不多,所以我猜测为跨域导致了两次调用的http上下文不一致造成的,当时我们解决跨域问题的方式是在服务端配置文件的system.webServer节点下加入:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
我以为是这种解决方式不够完善,于是我开始在网上寻找并尝试各种解决跨域的方案,最重型的尝试就是把我的.net4.0版本的web api升级到.net4.5的web api2来使用Microsoft ASP.NET Web API 2 Cross-Origin Suppor支持跨域,结果发现这种方法只是控制更精细,和我们最开始用的那种简单粗暴的方式没有本质上的差别,别的方法也都是大同小异,于是此路不通。
此时我想到自己定义一个简单的登录,具体实现就是:
1.登录时创建一个票据,并在服务端保存一组票据和用户信息的键值对,将票据返回给客户端,客户端在访问需要登录的接口时必须带上此票据
2.给需要登录验证的接口添加一个自定义的AuthorizeAttribute,在此属性中获取客户端传递的票据,来验证票据是否存在或者过期,如果票据合法,将对应的用户信息添加到http上下文,如果票据不合法,返回用户未登录的提示
由于本人功力有限,并且因为项目涉及到充值提现等资金操作,已经定了要使用SSL,所以关于篡改,复用等传输安全方面的问题没有纳入考虑。
以下是代码:
用户信息模型(登录时将返回此信息至客户端):
public class MemberTicket
{
public string ID { get; set; }
public string LoginName { get; set; }
public string Token { get; set; }
public DateTime LoginDate { get; set; }
}
登录处理类:
/// <summary>
/// 自定义登录
/// </summary>
public class LoginHelper
{
/// <summary>
/// 用户信息集合
/// </summary>
private static Dictionary<string, MemberTicket> Members = new Dictionary<string, MemberTicket>();
/// <summary>
/// 登录
/// </summary>
/// <param name="ticket">用户信息</param>
public static void Login(MemberTicket ticket)
{
if (Members.Keys.Contains(ticket.Token))
Members[ticket.Token] = ticket;
else
Members.Add(ticket.Token, ticket);
}
/// <summary>
/// 退出登录
/// </summary>
/// <param name="Token">票据</param>
public static void SignOut(string Token)
{
if (Members.Keys.Contains(Token))
Members.Remove(Token);
}
/// <summary>
/// 根据票据检查票据是否合法
/// </summary>
/// <param name="Token">票据</param>
/// <returns></returns>
public static MemberTicket Check(string Token)
{
if (!string.IsNullOrEmpty(Token) && Members.Keys.Contains(Token))
{
MemberTicket ticket = Members[Token];
if (ticket != null && ticket.LoginDate.AddMinutes(CommonData.TimeOut) > CommonData.TimeNow())
return ticket;
}
return null;
}
}
定义用户对象(能保存于http上下文的结构):
public class MemberPrincipal : IPrincipal
{
private string loginname; public string Loginname
{
get { return loginname; }
set { loginname = value; }
} private IIdentity _Identity; public IIdentity Identity
{
get { return _Identity; }
set { _Identity = value; }
} public bool IsInRole(string role)
{
return false;
} public MemberPrincipal(string Name)
{
loginname = Name;
_Identity = new GenericIdentity(loginname, "Forums");
bool isok = _Identity.IsAuthenticated;
}
}
保存用户信息到http上下文:
public class PrincipalHelper
{
public static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
}
关键部分,自定义登录策略:
public class LoginAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext httpContext)
{
MemberTicket ticket = LoginHelper.Check(GetToken(httpContext.Request.RequestUri.Query));
if (ticket == null)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK); ResponseMessage<string> result = new ResponseMessage<string>();
result.Header = new ResponseHeader();
result.Header.State = (int)ResponseHeaderState.SignOut;
result.Header.Message = "用户未登录";
response.Content = new StringContent(JsonConvert.SerializeObject(result)); httpContext.Response = response;
}
else
PrincipalHelper.SetPrincipal(new MemberPrincipal(ticket.LoginName)); } public string GetToken(string Query)
{
if (!Query.Contains("Token"))
return null;
string[] Param = Query.Split('&');
if (Param.Length == )
return null;
foreach (var item in Param)
{
if (!item.Contains("Token"))
continue;
string[] value = item.Split('=');
if (value.Length == )
return null;
return value[];
}
return null;
}
}
返回消息结构:
public class ResponseMessage<T>
{
/// <summary>
/// 消息头
/// </summary>
public ResponseHeader Header { get; set; }
/// <summary>
/// 消息本体
/// </summary>
public T Body { get; set; }
} public class ResponseHeader
{
/// <summary>
/// 执行状态
/// </summary>
public int State { get; set; }
/// <summary>
/// 消息
/// </summary>
public string Message { get; set; }
}
最后将此属性加在需要登录验证的控制器或方法上即可。
做完之后,目前是达到了我的预期,但是我的预期太低,所以自己也感觉弄得很low……希望各位大神小神大牛小牛不吝赐教,指点指点我应该往哪个方向优化登录机制
Web Api跨域登录问题的更多相关文章
- Web Api跨域访问配置及调用示例
1.Web Api跨域访问配置. 在Web.config中的system.webServer内添加以下代码: <httpProtocol> <customHeaders> &l ...
- ASP.NET Web API 跨域访问(CORS)
一.客户端用JSONP请求数据 如果你想用JSONP来获得跨域的数据,WebAPI本身是不支持javascript的callback的,它返回的JSON是这样的: {"YourSignatu ...
- ABP框架Web API跨域问题的解决方案
1.在Web Api 项目下安装 Microsoft.AspNet.WebApi.Cors 包 Install-Package Microsoft.AspNet.WebApi.Cors 2.在Web ...
- web api 跨域请求,ajax跨域调用webapi
1.跨域问题仅仅发生在Javascript发起AJAX调用,或者Silverlight发起服务调用时,其根本原因是因为浏览器对于这两种请求,所给予的权限是较低的,通常只允许调用本域中的资源,除非目标服 ...
- Web Api 跨域解决方案
一.跨域问题的由来 同源策略:出于安全考虑,浏览器会限制脚本中发起的跨站请求,浏览器要求JavaScript或Cookie只能访问同域下的内容. 正是由于这个原因,我们不同项目之间的调用就会被浏览器阻 ...
- Web API 跨域请求
分布式技术在项目中会频繁用到,以前接触过WebService(可跨平台).WCF(功能强大,配置繁琐), 最近由于上层业务调整,将原来的MVC项目一分为三,将数据层提取出来,用API去访问.然后 ...
- asp.net web api 跨域问题
缘起 以前在asp.net mvc时代,很少出现跨域问题 自从使用了asp.net web api + angular (1/2)之后,开始有跨域问题了. 简单普及下跨域: 我的理解是只要是前台页面与 ...
- ASP.NET web api 跨域请求
1.学习文章:AJAX 跨域请求 - JSONP获取JSON数据 1.asp.net代码 参考文章:http://www.sxt.cn/info-2790-u-756.html (1).增加CorsH ...
- Web API(五):Web API跨域问题
一.什么是跨域问题 跨域:指的是浏览器不能执行其他网站的脚本.是由浏览器的同源策略造成的,是浏览器施加的安全限制.(服务端可以正常接收浏览器发生的请求,也可以正常返回,但是由于浏览器的安全策略,浏览器 ...
随机推荐
- 集成学习_Bagging 和随机森林(rf)
集成学习方式总共有3种:bagging-(RF).boosting-(GBDT/Adaboost/XGBOOST).stacking 下面将对Bagging 进行介绍:(如下图所示) ...
- [CodeForces] CF226D The table
Harry Potter has a difficult homework. Given a rectangular table, consisting of n × m cells. Each ce ...
- 爬虫文件存储:txt文档,json文件,csv文件
5.1 文件存储 文件存储形式可以是多种多样的,比如可以保存成 TXT 纯文本形式,也可以保存为 Json 格式.CSV 格式等,本节我们来了解下文本文件的存储方式. 5.1.1 TXT文本存储 将数 ...
- Jupyter Notebook 下安装 PHP 内核
我最近被强烈安利了 Jupyter Notebook 这个交互式笔记本.然后试用了它自带的 Python 内核后,这个应用整体给我的感觉很不错,就去搜索了下它所支持的其它内核 Jupyter Kern ...
- 腾讯云,搭建LNMP环境
LNMP代表的就是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构. Linux是一类Unix计算机操作系统的统称,是目前最流行的免费操作系统.代表版本有:debian.centos ...
- .Net防sql注入的方法总结
#防sql注入的常用方法: 1.服务端对前端传过来的参数值进行类型验证: 2.服务端执行sql,使用参数化传值,而不要使用sql字符串拼接: 3.服务端对前端传过来的数据进行sql关键词过来与检测: ...
- selenium常用操作,查找元素,操作Cookie,获取截图,获取窗口信息,切换,执行js代码
目录: 1. 常用操作 2. 查找元素 3. 操作Cookie 4. 获取截图 5. 获取窗口信息 6. 切换 7. 执行JS代码 简介 selenium.webdriver.remote.webdr ...
- Maven学习总结(7)——eclipse中使用Maven创建Web项目
Maven学习总结(七)--eclipse中使用Maven创建Web项目 一.创建Web项目 1.1 选择建立Maven Project 选择File -> New ->Project,如 ...
- noip模拟赛 逃避
题目描述 给定一篇只含有大小写字母,空格以及 ′.′(不含引号)的长度为 L 的文章.文章被若干个 ′.′ 划分 成若干个句子,句子被若干个空格划分成单词.你需要将文章中每个句子第一个单词的首字母改成 ...
- 中缀表达式转逆波兰式(后缀表达式)求值 C++ Stack
给一个包含小数的中缀表达式 求出它的值 首先转换为后缀表达式然后利用stack求出值 转换规则: 如果字符为'(' push else if 字符为 ')' 出栈运算符直到遇到‘(' else if ...