欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.Net Core权限认证基于Cookie的认证&授权.Scheme、Policy扩展

程序员文章站 2023-11-13 13:30:10
在身份认证中,如果某个Action需要权限才能访问,最开始的想法就是,哪个Action需要权限才能访问,我们写个特性标注到上面即可,[TypeFilter(typeof(CustomAuthorizeActionFilterAttribute))] 当然了,要先在服务里面使用Session的服务== ......

在身份认证中,如果某个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权限认证基于Cookie的认证&授权.Scheme、Policy扩展

 

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

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

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

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

.Net Core权限认证基于Cookie的认证&授权.Scheme、Policy扩展

 

当然也要使用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

.Net Core权限认证基于Cookie的认证&授权.Scheme、Policy扩展

 

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

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

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

 .Net Core权限认证基于Cookie的认证&授权.Scheme、Policy扩展

 

 

通过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、控制器、全局三种注册方式。