最近项目第一次尝试使用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. Ansible实现zabbix服务器agent端批量部署

    项目需求:由于搭建zabbix,需要每台服务器都需要安装监控端(agent)正常的的操作是一台一台去安装,这样确实有点浪费时间,这里为大家准备了一款开源 的自动化运维工具Ansible,相信大家也很熟 ...

  2. vue和iview中native点击事件修饰

    在父组件中给子组件绑定一个原生的事件,就将子组件变成了普通的HTML标签,不加'. native'事件是无法触 在vue中使用iview的dropdownMenu 上单纯的@click也不生效,要写成 ...

  3. vue移动端地址三级联动组件(二)

    继续上一篇: 子组件css: <style scoped lang="less"> #city { width: 100%; height: 100%; positio ...

  4. HDU 2451 Simple Addition Expression(找规律,考验智商)

    题目 最近比赛的题目好多签到题都是找规律的考验智商的题目啊,,,我怎么越来越笨了,,,, 通过列举,可以发现规律: 从左往右按位扫这个数: 当数的长度大于1时: 当首位大于3时,答案就是4*4*4*… ...

  5. js兼用性

    1.document.formName.item("itemName") 问题 说明:IE下,可以使用document.formName.item("itemName&q ...

  6. 使用Scrapy爬取图书网站信息

    重难点:使用scrapy获取的数值是unicode类型,保存到json文件时需要特别注意处理一下,具体请参考链接:https://www.cnblogs.com/sanduzxcvbnm/p/1030 ...

  7. Django 初学

    一.什么是web框架 框架,即framework  ['fremwɝk],特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞 ...

  8. vue 项目的I18n国际化之路

    I18n (internationalization ) ---未完善 产品国际化是产品后期维护及推广中重要的一环,通过国际化操作使得产品能更好适应不同语言和地区的需求 国际化重点:1. 语言语言本地 ...

  9. vue项目中的常见问题(vue-cli版本3.0.0)

    一.样式问题 1.vue中使用less 安装less依赖 npm install less less-loader --save-dev 在使用时 在style标签中加入 lang="les ...

  10. python json结构

    =====================================================json==============================import reques ...