anglarjs1.6.3+owin 实现验证之一:统一拒绝非登录访问。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了anglarjs1.6.3+owin 实现验证之一:统一拒绝非登录访问。相关的知识,希望对你有一定的参考价值。

1、anglarjs端在app.js(即anglar的入口js),注册.factory("messageService",使得每次来自html客户端的请求都能带有一个值,如AKey:


var chars = [‘0‘,‘1‘,‘2‘,‘3‘,‘4‘,‘5‘,‘6‘,‘7‘,‘8‘,‘9‘,‘A‘,‘B‘,‘C‘,‘D‘,‘E‘,‘F‘,‘G‘,‘H‘,‘I‘,‘J‘,‘K‘,‘L‘,‘M‘,‘N‘,‘O‘,‘P‘,‘Q‘,‘R‘,‘S‘,‘T‘,‘U‘,‘V‘,‘W‘,‘X‘,‘Y‘,‘Z‘];
function generateMixed(n) {
var res = "";
for(var i = 0; i < n ; i ++) {
var id = Math.ceil(Math.random()*35);
res += chars[id];
}
return res;
}var ClientID=generateMixed(6);

var
Akey=ccc; expressApp.factory(authInterceptor, function($rootScope){ return { request: function(config){ config.headers = config.headers || {}; config.headers.authorization = Akey; return config; }, responseError: function(response){ if(response.status==403) { debugger location.href="./E403.html" } } }; }) expressApp.config(function ($locationProvider, $routeProvider,$httpProvider) { $httpProvider.interceptors.push(authInterceptor); }

2、html客户端做一个403页面,页面带一个连接,连接跳转到登录页

<li>
     使用具有访问权限的账户重新登录系统,点击:
    <a href="./index.html#/login">此处</a></li>

3、客户端包含一个登录页login.html,对应的关键js如下:

$http({
    method: post, url: baseUrl+account/login,
    data: {
        LoginName: $scope.LoginName,
        Password:$scope.Password,
        ClientID:ClientID
    }
}).then(function (response) {
        Akey=response.data.LogonUser.SessionKey;
    },
    function (response) {
    }
);

这里面最重要的一句话,就是Akey=response.data.LogonUser.SessionKey; 因为Aky是在app.js里面定义的全局变量,所以登录之前是一个错误值,在服务端限定以后,不能访问任何常规的action,但是只能访问服务端的account/login这个action。

4、下来看看服务端的login这个action

[Route("express/account/login")]
public HttpResponseMessage Login(Users user)
{
    if (string.IsNullOrEmpty(user.LoginName))
        return Request.CreateResponse(HttpStatusCode.Forbidden);
    if (string.IsNullOrEmpty(user.Password))
        return Request.CreateResponse(HttpStatusCode.Forbidden);

    var nowUser = auS.GetUserByUserId(user.LoginName);
    if (nowUser == null)
        return Request.CreateResponse(HttpStatusCode.Forbidden);

    #region 验证密码
    if (!string.Equals(nowUser.Password, user.Password))
    {
        return Request.CreateResponse(HttpStatusCode.Forbidden);
    }
    #endregion

    if (nowUser.IsDelete)
        return Request.CreateResponse(HttpStatusCode.Forbidden);

    var existsDevice = auS.GetClientUser(nowUser.LoginName);

    if (existsDevice == null)
    {
        string passkey = auS.ComputeHash(nowUser.LoginName + nowUser.ClientID + DateTime.UtcNow + Guid.NewGuid());
        existsDevice = new Users()
        {
            LoginName = nowUser.LoginName,
            SessionKey = passkey,
            ClientID = user.ClientID
        };
        auS.AddClientUser(existsDevice);
    }
    else
    {

        auS.UpdateUserDevice(existsDevice);
    }
    existsDevice.Password = "";

    return Request.CreateResponse(HttpStatusCode.OK, new SessionObject() { SessionKey = existsDevice.SessionKey, LogonUser = existsDevice });
}

 这里面最关键的一个就是这两句话,当客户端发来一个clientID以后,使用一定的规则合成一个会话ID,存到内存中一个静态列表里面,以实现只要登录一次且不超时,就缓存这个人的登录信息,相当于session一样的效果。

string passkey = auS.ComputeHash(nowUser.LoginName + nowUser.ClientID + DateTime.UtcNow + Guid.NewGuid());
ClientID = user.ClientID

那么在来看看app.js里面的这个ClientID:

var chars = [0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z];

function generateMixed(n) {
     var res = "";
     for(var i = 0; i < n ; i ++) {
         var id = Math.ceil(Math.random()*35);
         res += chars[id];
     }
     return res;
}
var ClientID=generateMixed(6);

5、来看看AuthenticationService.cs的一些关键代码:这里也可以用字典,甚至可以把列表迁移到redis上面去都可以。

public string ComputeHash(string input)
{
    byte[] data = Md5.ComputeHash(Encoding.Unicode.GetBytes(input));
    var sBuilder = new StringBuilder();
    foreach (byte t in data)
        sBuilder.Append(t.ToString("X"));
    return sBuilder.ToString();
}
 /// <summary>
/// 使用唯一标识获得登录用户
/// </summary>
/// <param name="Akey"></param>
/// <returns></returns>
public Users GetClientUserByAkey(string Akey)
{
    //headers.Authorization.Scheme
    var ul = ClientUser.Where(_ =>
    {
        return _.SessionKey == Akey;
    });
    if (ul != null && ul.Count() > 0)
    {
        var u = ul.FirstOrDefault();
        if (u.ExpiredTime < DateTime.Now)
        {
            return u;
        }
        else
        {
            ClientUser.Remove(u);
        }
    }
    return null;
}

public Users GetUserByUserId(string loginName)
{

  //Accessor是我写的一个ORM的访问器
  var ul = Accessor.GetList<Users>(new { LoginName = loginName });
  return ul.FirstOrDefault();
}

 

首先BaseController继承自ApiController,而且用于登录的那个Controller也就是AccountController必须继承自 ApiController

AccountController : ApiController

BaseController: ApiController

再来看看BaseController的关键代码,这样我们写业务Controller(非登录AccountController)的时候,比如ProductController,让ProductController:BaseController即可

public Express.Entity.Users currentUser = null;
public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
{
    if (controllerContext.Request.Headers.Authorization != null
        && !string.IsNullOrWhiteSpace(controllerContext.Request.Headers.Authorization.Scheme))
    {
        currentUser = new AuthenticationService().GetClientUserByAkey(controllerContext.Request.Headers.Authorization.Scheme);
        if (currentUser == null)
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }
    }
    else
    {
        var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
        var tsc = new TaskCompletionSource<HttpResponseMessage>();
        tsc.SetResult(response);
        return tsc.Task;
    }
    return base.ExecuteAsync(controllerContext, cancellationToken);
}

好了,下一篇讲述,如果实现粒度到action级别的权限控制。

以上是关于anglarjs1.6.3+owin 实现验证之一:统一拒绝非登录访问。的主要内容,如果未能解决你的问题,请参考以下文章

身份验证/授权 MVC 5 和 Web API - Katana/Owin

OWIN OpenIdConnect 中间件 IDX10311 随机数无法验证

在与一个 IIS 应用程序连接的两个域之间共享 OWIN 身份验证 Cookie

OWIN 安全 - 如何实现 OAuth2 刷新令牌

Auth0 - 在 Owin 上使用带有承载访问令牌的 JWT 使用 RS256 进行身份验证

WebApi 上的 AngularJS 和 OWIN 身份验证