.net MVC下鉴权认证(三)

上次讲了.net core下的自带的鉴权认证中间件来实现权限的管理,不过只是针对普通mvc项目,客户端信息时通过cookie来实现,如果是前后端分离的项目那是用不了的,一般我们是需要通过JWT来实现。

那在自带的鉴权中间件如何使用JWT呢,这里的环境使用的是vs2019 + .net core3.1

需要安装System.IdentityModel.Tokens.Jwt和Microsoft.AspNetCore.Authentication.JwtBearer

 在startup中配置认证模式

public void ConfigureServices(IServiceCollection services) {
            services.AddControllers();

            //注入JWT服务
            services.AddScoped<IJwtService, JwtService>();
            services.AddSingleton<IAuthorizationHandler, CustomAuthorizationHandler>();

            //jwt认证服务配置
            var issuer = Configuration["issuer"];
            var audience = Configuration["audience"];
            var securityKey = Configuration["SecurityKey"];
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)//jwt的授权机制名称
                .AddJwtBearer(op => {
                    op.TokenValidationParameters = new TokenValidationParameters {
                        ValidateIssuer = true,//是否进行相关参数验证
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidAudience = audience,
                        ValidIssuer = issuer,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey)),
                        ClockSkew = TimeSpan.Zero,//这个是缓冲过期时间,也就是说,即使我们配置了过期时间,这里也要考虑进去,过期时间+缓冲,默认好像是7分钟,你可以直接设置为0
                        RequireExpirationTime = true,
                    };
                });
            services.AddAuthorization(op => {
                op.AddPolicy("policy1", p => {
                    p.AddRequirements(new CustomAuthorizatinRequirement());
                });
            });

            //Swagger文档配置使用http://localhost:5001/swagger
            //注册Swagger生成器,定义一个和多个Swagger 文档
            services.AddSwaggerGen(b =>
            {
                b.SwaggerDoc("v1", new OpenApiInfo() {
                    Title = "api接口调用文档",
                    Version = "v1",
                    Description = "系统相关Api文档"
                });
                //为Swagger Json and UI设置XML文档的路径
                var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);//获取应用程序的路径
                var xmlPath = Path.Combine(basePath, "Swagger.xml");
                b.IncludeXmlComments(xmlPath);
            });

        }

定义jwt服务接口

    public interface IJwtService {
        string GetToken(string account);
    }

 实现此接口

using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace JwtDemo.Service {
    public class JwtService : IJwtService {
        private readonly IConfiguration _configuration;
        public JwtService(IConfiguration configuration) {
            _configuration = configuration;
        }

        public string GetToken(string account) {
            /**
             * Claims(Payload)
             * Claims包含了一些跟这个token有关的重要信息。JWT标准规定的字段:
             * 
             * iss: The issuer of the token, 签发人
             * sub: The subject of the token, 主题
             * exp: Expiration Time. 过期时间(Unix时间戳格式)
             * iat: Issued At. 签发时间(Unix时间戳格式)
             * jti: JWT ID. 编号
             * aud: audience. 受众
             * nbf: Not Before. 生效时间
             * 
             * 除了规定的字段外,可以包含其他任何JSON兼容的字段。
             * */
            var roleList = new List<string>() {
                    "admin",
                    "test"
                };

            var claims = new List<Claim>()
            {
                new Claim(ClaimTypes.Name, account),
                new Claim("account", account),
                //new Claim(ClaimTypes.Role, "admin")
            };
            //填充角色
            foreach (var item in roleList) {
                claims.Add(new Claim(ClaimTypes.Role, item));
            }
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["SecurityKey"]));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                issuer: _configuration["issuer"], //签发人
                audience: _configuration["audience"],//接受方
                claims: claims,
                expires: DateTime.Now.AddMinutes(20), //20分钟有效期
                signingCredentials: credentials);

            var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
            return tokenStr;
        }
    }
}

 启用鉴权中间件,这个和之前的方法一样

            app.UseAuthentication();//启用鉴权中间件

在appsettings.json中配置jwt信息

  "SecurityKey": "58CAA81E-1511-EF1F-8C09-4425F7362F11",
  "issuer": "http://localhost:5000/",
  "audience": "http://localhost:5000/"

编写登录webapi

using JwtDemo.DTO;
using JwtDemo.Service;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace JwtDemo.Controllers {
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class LoginController : Controller {
        private readonly IJwtService _jwtService;
        public LoginController(IJwtService jwtService) {
            _jwtService = jwtService;
        }
            
        /// <summary>
        /// 获取登录的token
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost]
        public IActionResult DoLogin([FromBody] LoginRequestDTO dto) {
            bool success = false;
            string token;
            if(dto.Account=="1646" && dto.Password == "123") {
                success = true;
                token = _jwtService.GetToken(dto.Account);
            }
            else {
                token = "";
            }
            return Json(new {
                success,
                token
            });
        }
    }
}

通过此DoLogin的webapi我们可以获取到token,那就说明我们可以取到token了。

 然后我们来添加一个测试访问的webapi

        /// <summary>
        /// 测试
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Authorize(Roles ="admin")]
        public IActionResult Test() {
            var claims = HttpContext.AuthenticateAsync().Result.Principal.Claims;
            var name = claims.FirstOrDefault(b => b.Type.Equals(ClaimTypes.Name))?.Value;
            var account = claims.FirstOrDefault(b => b.Type.Equals("Account"))?.Value;//null检查运算符 不为空时执行
            var role = claims.FirstOrDefault(b => b.Type.Equals("Role"))?.Value;
            var exp = claims.FirstOrDefault(b => b.Type.Equals("exp"))?.Value;

            var expDateTime = DateTime.Now;
            if (!string.IsNullOrWhiteSpace(exp)) {
                long expValue;
                if (long.TryParse(exp, out expValue)) {
                    expDateTime = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), TimeZoneInfo.Local);
                    expDateTime = expDateTime.AddSeconds(expValue);
                }
            }

            return Json(new {
                ExpDateTime = expDateTime,
                Name = name,
                Data = "已授权",
                Type = "GetAuthorizeData"
            });
        }

我们通过postman来测试,直接访问Test这个webapi,会提示401错误,然后我们通过设置authorization的type选择bear token,然后将我们刚才获取到的token输入到里面,然后再发起请求就可以访问成功了。

 

角色管理 

使用jwt同样也存在角色管理这个问题,我们可以在控制器或action上加上特性[Authorize(Roles ="admin")]来设置可以访问的角色,如果刚才我的测试登录的用户未给到admin角色,那么我访问就会报403没有权限的错误。加上admin角色就可以访问成功了。

自定义策略授权进阶

使用jwt同样也存在,如果角色写在代码里也不好控制,同样我们也可以通过自定义策略来实现。方法其实跟之前是一样的。

 1.添加CustomAuthorizatinRequirement.cs这个类实现接口IAuthorizationRequirement,这个IAuthorizationRequirement是个空接口,我们直接实现不用写什么代码。

    public class CustomAuthorizatinRequirement : IAuthorizationRequirement {

    }

2.添加CustomAuthorizationHandler.cs这个认证处理类,这个类需要实现IAuthorizationRequirement这个接口,不过一般我们都是通过继承AuthorizationHandler<T>泛型抽象类来实现。

    public class CustomAuthorizationHandler: AuthorizationHandler<CustomAuthorizatinRequirement> {
        private IHttpContextAccessor _httpContext;
        public CustomAuthorizationHandler(IHttpContextAccessor httpContext) {
            _httpContext = httpContext;
        }

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomAuthorizatinRequirement requirement) {
            bool flag=false;
            var httpContext = _httpContext.HttpContext;
            RouteEndpoint res = context.Resource as RouteEndpoint;
            if (res != null) {
                if (res.RoutePattern.RequiredValues.Keys.Count() == 2){
                    var action = res.RoutePattern.RequiredValues.ToList().FirstOrDefault(b => b.Key == "action").Value;
                    var ctr = res.RoutePattern.RequiredValues.ToList().FirstOrDefault(b => b.Key == "controller").Value;
                }
            }

            var user = context.User.Claims.FirstOrDefault(b => b.Type == "account").Value;

            if (user == "1646") {
                flag = true;
            }
            if (flag) {
                context.Succeed(requirement);
            }
            return Task.CompletedTask;
        }
    }

3.让自定义策略生效,需要在服务注册services.AddSingleton<IAuthorizationHandler, CustomAuthorizationHandler>();还有策略的注册

        public void ConfigureServices(IServiceCollection services) {
            services.AddControllersWithViews();

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            //自定义认证处理的服务注册
            services.AddSingleton<IAuthorizationHandler, CustomAuthorizationHandler>();
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(op => {
                    op.LoginPath = new PathString("/Login");//登录路径
                    op.AccessDeniedPath = new PathString("/Login/Denied");//无权限地址
                });
            //策略的注册
            services.AddAuthorization(op => {
                op.AddPolicy("policy1", p => {
                    p.AddRequirements(new CustomAuthorizatinRequirement());
                });
            });
        }

然后在控制器上加上

 [HttpGet]
        [Authorize(policy:"policy1")]
        public IActionResult TestA() {
            var claims = HttpContext.AuthenticateAsync().Result.Principal.Claims;
            var name = claims.FirstOrDefault(b => b.Type.Equals(ClaimTypes.Name))?.Value;
            var account = claims.FirstOrDefault(b => b.Type.Equals("Account"))?.Value;//null检查运算符 不为空时执行
            var role = claims.FirstOrDefault(b => b.Type.Equals("Role"))?.Value;
            var exp = claims.FirstOrDefault(b => b.Type.Equals("exp"))?.Value;

            var expDateTime = DateTime.Now;
            if (!string.IsNullOrWhiteSpace(exp)) {
                long expValue;
                if (long.TryParse(exp, out expValue)) {
                    expDateTime = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), TimeZoneInfo.Local);
                    expDateTime = expDateTime.AddSeconds(expValue);
                }
            }

            return Json(new {
                ExpDateTime = expDateTime,
                Name = name,
                Data = "已授权",
                Type = "GetAuthorizeData"
            });
        }

这样我访问TestA这个webapi的时候会根据CustomAuthorizationHandler这个处理类来进行授权判断,具体判断业务逻辑当然你可以自己组织了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值