最近项目第一次尝试使用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跨域登录问题的更多相关文章

  1. Web Api跨域访问配置及调用示例

    1.Web Api跨域访问配置. 在Web.config中的system.webServer内添加以下代码: <httpProtocol> <customHeaders> &l ...

  2. ASP.NET Web API 跨域访问(CORS)

    一.客户端用JSONP请求数据 如果你想用JSONP来获得跨域的数据,WebAPI本身是不支持javascript的callback的,它返回的JSON是这样的: {"YourSignatu ...

  3. ABP框架Web API跨域问题的解决方案

    ​1.在Web Api 项目下安装 Microsoft.AspNet.WebApi.Cors 包 Install-Package Microsoft.AspNet.WebApi.Cors 2.在Web ...

  4. web api 跨域请求,ajax跨域调用webapi

    1.跨域问题仅仅发生在Javascript发起AJAX调用,或者Silverlight发起服务调用时,其根本原因是因为浏览器对于这两种请求,所给予的权限是较低的,通常只允许调用本域中的资源,除非目标服 ...

  5. Web Api 跨域解决方案

    一.跨域问题的由来 同源策略:出于安全考虑,浏览器会限制脚本中发起的跨站请求,浏览器要求JavaScript或Cookie只能访问同域下的内容. 正是由于这个原因,我们不同项目之间的调用就会被浏览器阻 ...

  6. Web API 跨域请求

    分布式技术在项目中会频繁用到,以前接触过WebService(可跨平台).WCF(功能强大,配置繁琐),    最近由于上层业务调整,将原来的MVC项目一分为三,将数据层提取出来,用API去访问.然后 ...

  7. asp.net web api 跨域问题

    缘起 以前在asp.net mvc时代,很少出现跨域问题 自从使用了asp.net web api + angular (1/2)之后,开始有跨域问题了. 简单普及下跨域: 我的理解是只要是前台页面与 ...

  8. ASP.NET web api 跨域请求

    1.学习文章:AJAX 跨域请求 - JSONP获取JSON数据 1.asp.net代码 参考文章:http://www.sxt.cn/info-2790-u-756.html (1).增加CorsH ...

  9. Web API(五):Web API跨域问题

    一.什么是跨域问题 跨域:指的是浏览器不能执行其他网站的脚本.是由浏览器的同源策略造成的,是浏览器施加的安全限制.(服务端可以正常接收浏览器发生的请求,也可以正常返回,但是由于浏览器的安全策略,浏览器 ...

随机推荐

  1. sysbench测试阿里云CPU

    参考 https://wiki.mikejung.biz/Benchmarking 买了一个1核的ECS,测试一下CPU性能 第一次是只用1个thread去跑 [root@iZwz9fy718twfi ...

  2. =、==、is、id(内容)

    = 赋值 == 比较值是否相等 is 比较.比较的是内存地址 id(内容) 测出内存地址

  3. 【codeforces 514B】Han Solo and Lazer Gun

    [题目链接]:http://codeforces.com/contest/514/problem/B [题意] 每次攻击可以把经过自己的一条直线上的所有点都毁掉; 然后给你n个目标物的坐标 问你最少要 ...

  4. [bzoj3207]花神的嘲讽计划Ⅰ[可持久化线段树,hash]

    将每k个数字求一个哈希值,存入可持久化线段树,直接查询即可 #include <iostream> #include <algorithm> #include <cstd ...

  5. Spring MVC Beginner's Guide--应该看第二次

    第一遍,就差WEBFLOW知识点没过了.. 真的值得好好再看第二次呢.. 样例工程算是比较多的啦. 学到真的不少..

  6. 虚拟机+centOS挂载ISO步骤

    https://blog.csdn.net/u010612373/article/details/52240447

  7. 洛谷——P2871 [USACO07DEC]手链Charm Bracelet

    https://www.luogu.org/problem/show?pid=2871 题目描述 Bessie has gone to the mall's jewelry store and spi ...

  8. [转]十五天精通WCF——终结篇 那些你需要注意的坑

    终于一路走来,到了本系列的最后一篇了,这一篇也没什么好说的,整体知识框架已经在前面的系列文章中讲完了,wcf的配置众多,如果 不加一些指定配置,你可能会遇到一些灾难性的后果,快来一睹为快吧. 一: 第 ...

  9. Openfire:解决乱码问题

    当部署openfire后,创建用户和发送离线消息时会出现中文字符乱码的问题.要解决这个问题需要同时配置openfire和mysql两端. 首先openfire端,在安装页面中指定odbc连接串中需要带 ...

  10. wait()方法写在while循环中可以在线程接到通知后再一次判断条件

    wait()方法写在while循环中可以在线程接到通知后再一次判断条件 synchronized public String pop() { String returnValue = "&q ...