在身份认证中,如果某个action需要权限才能访问,最开始的想法就是,哪个action需要权限才能访问,我们写个特性标注到上面即可,[typefilter(typeof(customauthorizeactionfilterattribute))]

/// <summary>
 /// 这是一个action的filter`  但是用作权限验证
 /// </summary>
 public class customauthorizeactionfilterattribute : attribute, iactionfilter
 {
     private ilogger<customauthorizeactionfilterattribute> _logger = null;
     public customauthorizeactionfilterattribute(ilogger<customauthorizeactionfilterattribute> logger)
     {
         this._logger = logger;
     }

     public void onactionexecuting(actionexecutingcontext context)
     {
         //取出session
         var struser = context.httpcontext.session.getstring("currentuser");
         if (!string.isnullorwhitespace(struser))
         {
             currentuser currentuser = newtonsoft.json.jsonconvert.deserializeobject<currentuser>(struser);
             _logger.logdebug($"username is {currentuser.name}");
         }
         else
         { 
             context.result = new redirectresult("~/fourth/login");
         }
          
     }
     public void onactionexecuted(actionexecutedcontext context)
     {
         //context.httpcontext.response.writeasync("actionfilter executed!");
         console.writeline("actionfilter executed!");
         //this._logger.logdebug("actionfilter executed!");
     }

 }

当然了,要先在服务里面使用session的服务==》services.addsession();

但是这样不好。.net core框架下,有一个特性authorize,当我们需要使用的时候,在某个action上面标注即可

 [authorize]
 public iactionresult center()
 {
     return content("center");
 }

我们来运行看一下,会报异常

 

 因为我们没有使用服务,在.net core下面,是默认不启用授权过滤器的。这也是.net core框架的一个好处,我们需要的时候才进行使用。框架做的少,更轻。

下面我们在服务里面使用授权过滤器的服务

services.addauthentication(cookieauthenticationdefaults.authenticationscheme).
    addcookie(cookieauthenticationdefaults.authenticationscheme,
o =>
    {
        o.loginpath = new pathstring("/home/login");
    });

再次浏览刚才的页面,这样就会请求到登录页面,会把刚才请求的页面当做一个参数

 

当然也要使用app.useauthentication();这个中间件。

在.net core里面,保存登录状态,也是通过cookie的方式。使用claimsidentity与claimsprincipal

public actionresult login(string name, string password)
{
    this._ilogger.logdebug($"{name} {password} 登陆系统");
    #region 这里应该是要到数据库中查询验证的
    currentuser currentuser = new currentuser()
    {
        id = 123,
        name = "bingle",
        account = "administrator",
        password = "123456",
        email = "415473422@qq.com",
        logintime = datetime.now,
        role = name.equals("bingle") ? "admin" : "user"
    };
    #endregion

    #region cookie
    {
        ////就很像一个currentuser,转成一个claimidentity
        var claimidentity = new claimsidentity("cookie");
        claimidentity.addclaim(new claim(claimtypes.nameidentifier, currentuser.id.tostring()));
        claimidentity.addclaim(new claim(claimtypes.name, currentuser.name));
        claimidentity.addclaim(new claim(claimtypes.email, currentuser.email));
        claimidentity.addclaim(new claim(claimtypes.role, currentuser.role));
        claimidentity.addclaim(new claim(claimtypes.sid, currentuser.id.tostring()));
        var claimsprincipal = new claimsprincipal(claimidentity);
        base.httpcontext.signinasync(claimsprincipal).wait();//不就是写到cookie
    }
    #endregion

    return view();
}

再次进行登录,我们就可以看到这样一个cookie

 

 在这之后,我们再去访问genter页面,发现还是和之前返回的结果一样,还是访问不到。这是为什么呢?是因为我们在action上面打的标签[authorize],什么都没给,我们做下修改

 [authorize(authenticationschemes = cookieauthenticationdefaults.authenticationscheme)]
 public iactionresult center()
 {
     return content("center");
 }

现在我们再次进行访问,发现就可以访问成功了

 

 

 

通过user.findfirstvalue(claimtypes.sid);这种方式,可以获取到我们存入的值。

scheme、policy扩展

scheme

#region 设置自己的schema的handler 
 services.addauthenticationcore(options => options.addscheme<myhandler>("myscheme", "demo myscheme"));
 #endregion
 #region  schame 验证

 services.addauthentication(options =>
 {
     options.defaultscheme = cookieauthenticationdefaults.authenticationscheme;// "richard";//  
 })
 .addcookie(options =>
 {
     options.loginpath = new pathstring("/fourth/login");// 这里指定如果验证不通过就跳转到这个页面中去
     options.claimsissuer = "cookie";
 });

myhandler类:

/// <summary>
/// 自定义的handler
/// 通常会提供一个统一的认证中心,负责证书的颁发及销毁(登入和登出),而其它服务只用来验证证书,并用不到singin/singout。
/// </summary>
public class myhandler : iauthenticationhandler, iauthenticationsigninhandler, iauthenticationsignouthandler
{
    public authenticationscheme scheme { get; private set; }
    protected httpcontext context { get; private set; }

    public task initializeasync(authenticationscheme scheme, httpcontext context)
    {
        scheme = scheme;
        context = context;
        return task.completedtask;
    }

    /// <summary>
    /// 认证
    /// </summary>
    /// <returns></returns>
    public async task<authenticateresult> authenticateasync()
    {
        var cookie = context.request.cookies["mycookie"];
        if (string.isnullorempty(cookie))
        {
           return  authenticateresult.noresult();
        }
        return authenticateresult.success(this.deserialize(cookie));
    }

    /// <summary>
    /// 没有登录 要求 登录 
    /// </summary>
    /// <param name="properties"></param>
    /// <returns></returns>
    public task challengeasync(authenticationproperties properties)
    {
        context.response.redirect("/login");
        return task.completedtask;
    }

    /// <summary>
    /// 没权限
    /// </summary>
    /// <param name="properties"></param>
    /// <returns></returns>
    public task forbidasync(authenticationproperties properties)
    {
        context.response.statuscode = 403;
        return task.completedtask;
    }

    /// <summary>
    /// 登录
    /// </summary>
    /// <param name="user"></param>
    /// <param name="properties"></param>
    /// <returns></returns>
    public task signinasync(claimsprincipal user, authenticationproperties properties)
    {
        var ticket = new authenticationticket(user, properties, scheme.name);
        context.response.cookies.append("mycookie", this.serialize(ticket));
        return task.completedtask;
    }

    /// <summary>
    /// 退出
    /// </summary>
    /// <param name="properties"></param>
    /// <returns></returns>
    public task signoutasync(authenticationproperties properties)
    {
        context.response.cookies.delete("mycookie");
        return task.completedtask;
    }
    private authenticationticket deserialize(string content)
    {
        byte[] byteticket = system.text.encoding.default.getbytes(content);
        return ticketserializer.default.deserialize(byteticket);
    }

    private string serialize(authenticationticket ticket)
    {

        //需要引入  microsoft.aspnetcore.authentication

        byte[] byteticket = ticketserializer.default.serialize(ticket);
        return encoding.default.getstring(byteticket);
    }
}

 

policy

 #region 支持 policy 认证授权的服务  

 // 指定通过策略验证的策略列
 services.addsingleton<iauthorizationhandler, advancedrequirement>();

 services.addauthorization(options =>
 {
     //advancedrequirement可以理解为一个别名
     options.addpolicy("advancedrequirement", policy =>
     {
         policy.addrequirements(new nameauthorizationrequirement("1"));
     });
 }).addauthentication(options =>
 {
     options.defaultscheme = cookieauthenticationdefaults.authenticationscheme;
 })
 .addcookie(options =>
 {
     options.loginpath = new pathstring("/fourth/login");
     options.claimsissuer = "cookie";
 });

 #endregion

advancedrequirement类:

 /// <summary>
 /// policy 的策略 或者是规则
 /// </summary>
 public class advancedrequirement : authorizationhandler<nameauthorizationrequirement>, iauthorizationrequirement
 { 
     protected override task handlerequirementasync(authorizationhandlercontext context, nameauthorizationrequirement requirement)
     {
         // 这里可以把用户信息获取到以后通过数据库进行验证
         // 这里就可以做一个规则验证
         // 也可以通过配置文件来验证
         if (context.user != null && context.user.hasclaim(c => c.type == claimtypes.sid))
         {
             string sid = context.user.findfirst(c => c.type == claimtypes.sid).value;
             if (!sid.equals(requirement.requiredname))
             {
                 context.succeed(requirement);
             }
         }

         return task.completedtask;
     }
 }

 

还需要在configure方法中对中间件进行使用

app.usesession();
app.usecookiepolicy(); //
app.useauthentication(); // 标识在当前系统中使用这个权限认证

 总结:

  在.net framwork环境授权一般来说是这个样子的,在登录的时候写入session,在需要控制权限的方法上标机一个权限特性,实现在方法执行前对session进行判断,如果有session,就有权限。但是这种方式比较局限。

  .net core下的权限认证,来自于authenticationhttpcontextextensions扩展。

  6大方法,可以自行扩展这6个方法:需要自定义一个handler,handler需要继承实现iauthenticationhandler,iauthenticationsigninhandler,iauthenticationsignouthandler。分别实现6个方法,需要制定在core中使用。services.addauthenticationcore(options => options.addscheme<myhandler>(“myscheme”, “demo myscheme”));

  如果使用了sechme验证,验证不通过的时候,就默认跳转到account/login?returnurl=……。权限验证来自于iauthentizedata:authenticationschemes policy roles。权限验证支持action、控制器、全局三种注册方式。