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跨域问题
一.什么是跨域问题 跨域:指的是浏览器不能执行其他网站的脚本.是由浏览器的同源策略造成的,是浏览器施加的安全限制.(服务端可以正常接收浏览器发生的请求,也可以正常返回,但是由于浏览器的安全策略,浏览器 ...
随机推荐
- Configuring SSL on Enterprise Manager and the SLB (Release 12.1.0.2 and later)
From: http://docs.oracle.com/html/E24089_42/ha_setup.htm#sthref833 If the SLB is configured to use T ...
- Git下的标签
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本.将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来.所以,标签也是版本库的一个快照 ...
- 手写DAO框架(二)-开发前的最后准备
-------前篇:手写DAO框架(一)-从“1”开始 --------- 前言:前篇主要介绍了写此框架的动机,把主要功能点大致介绍了一下.此篇文章主要介绍开发前最后的一些准备.主要包括一些基础知识点 ...
- 赛门铁克通配符SSL证书,一张通配型证书实现全站加密
赛门铁克通配型SSL证书,验证域名所有权和企业信息,属于企业验证(OV) 级SSL证书,最高支持256位加密.申请通配符SSL证书可以保护相同主域名下无限数量的多个子域名(主机).例如,一个通配符 ...
- 【郑轻邀请赛 I】这里是天堂!
[题目链接]:https://acm.zzuli.edu.cn/zzuliacm/problem.php?id=2135 [题意] [题解] 答案应该为C(n,a)∗C(m,b)/C(n+m,a+b) ...
- 【[Offer收割]编程练习赛12 A】歌德巴赫猜想
[题目链接]:http://hihocoder.com/problemset/problem/1493 [题意] [题解] 枚举P从2..n/2 如果P是质数且N-P也是质数; 则输出P和N-P就好; ...
- Maven学习总结(3)——使用Maven构建项目
Maven学习总结(三)--使用Maven构建项目 maven作为一个高度自动化构建工具,本身提供了构建项目的功能,下面就来体验一下使用maven构建项目的过程. 一.构建Jave项目 1.1.创建J ...
- 清北学堂模拟赛d3t2 b
分析:一道比较让人头疼的数学题. 先考虑怎么让分出来的三角形相似,先不考虑每个三角形的具体边长,设每个三角形的周长为li,则可知必然有一个数g = gcd{li},每一个三角形的周长都是g的倍数,这样 ...
- wait-notify模型面试题
一道面试题: 启动两个线程, 一个输出 1,3,5,7-99, 另一个输出 2,4,6,8-100 最后 STDOUT 中按序输出 1,2,3,4,5-100 错误实现1: public class ...
- UVA 11825 状态压缩DP+子集思想
很明显的状态压缩思想了.把全集分组,枚举每个集合的子集,看一个子集是否能覆盖所有的点,若能,则f[s]=max(f[s],f[s^s0]+1).即与差集+1比较. 这种枚举集合的思想还是第一次遇到,果 ...