NET Core 1.1中使用Jwt

NET Core里Jwt的生成倒是不麻烦,就是要踩完坑才知道正确的生成姿势……

Jwt的结构

jwt的结构是{Header}.{Playload}.{Signature}三截。其中Header和Playload是base64编码字符串,Signature是签名字符串。

Header是比较固定的

typ是固定的“JWT”。

alg是你使用的签名算法,通常有HS256和RS256两种。

例子:

{
  "alg": "RS256",
  "typ": "JWT"
}

Playload你要写入自定义内容的区域。

例子:

{
  "role": "myRole",
  "org": "myOrg",
  "jti": "a8b8ea421e834fd1b90ac09dbf40e158",
  "nbf": 1498397026,
  "exp": 1498483426,
  "iat": 1498397026,
  "iss": "Zonciu"
}

Signature是使用Header中alg的算法来对{Header}.{Playload}这个结构进行签名生成一个字符串,然后接到Jwt串的最后,形成带签名的Jwt。在签发Token之后,其他应用就可以使用相同的算法和Key来重新运算并对比签名,由此判断Token中的信息是否被修改过。

注意:Jwt默认是明文的,不要把敏感数据放到playload里去,当然你可以先把要放进去的数据先加密,把密文放到playload里(但是最好不要这样做)。

Jwt创建过程

在NET Core 1.1里是通过JwtSecurityTokenHandler(程序集System.IdentityModel.Tokens.Jwt)这个类来创建Jwt。

但是创建Jwt之前还要先生成一个SecurityTokenDescriptor(程序集Microsoft.IdentityModel.Tokens),所以就比较绕。

Jwt工厂代码:

我这里用的是RS256签名(RsaSecurityKey),是用私钥签名,公钥验证。如果是要用HS256的话,就把签名和验证的SecurityKey都换成SymmetricSecurityKey(如果我没记错的话)

这里的公钥密钥是方便测试所以硬编码的,生产环境不要这样搞。

public class JwtFactory
    {
        public const string publicKey = @"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArM1i3Q9ukD7DWhuYWFFH
fAR0Ao8W5OnrlaZH2aB2G+dgvrlW6VzFVtjZQLWkQ488j65MTS7Nr0GITsoGB5r4
LRdnua5PwkLCML9ZaqOejMYix4mc7ZfencsQvy5bHotfvEAad42IhvHROseqC77W
5Zbt+YDtA7aU2aBKzHufZ1vPgWKPOgGVJup6sjqviXz3qP2HD5K9ae0iyYDptKKN
e5kb36DNTD7P62yWrVpZpy0MpMkCBZJdDeUgtA3lsxY5FcEaB5Bk+O695djogq84
vsyTKP1Jp6GrgszIuJCb52dI5c1lY5tN6bxsMTYB/Hxhgo7dGG/LBU9lMoT83E15
KQIDAQAB
-----END PUBLIC KEY-----
";

        public const string privateKey = @"-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEArM1i3Q9ukD7DWhuYWFFHfAR0Ao8W5OnrlaZH2aB2G+dgvrlW
6VzFVtjZQLWkQ488j65MTS7Nr0GITsoGB5r4LRdnua5PwkLCML9ZaqOejMYix4mc
7ZfencsQvy5bHotfvEAad42IhvHROseqC77W5Zbt+YDtA7aU2aBKzHufZ1vPgWKP
OgGVJup6sjqviXz3qP2HD5K9ae0iyYDptKKNe5kb36DNTD7P62yWrVpZpy0MpMkC
BZJdDeUgtA3lsxY5FcEaB5Bk+O695djogq84vsyTKP1Jp6GrgszIuJCb52dI5c1l
Y5tN6bxsMTYB/Hxhgo7dGG/LBU9lMoT83E15KQIDAQABAoIBACvHrXCMZFqvTBcc
PrDBhvboueucDRTaHxG/Gx0MBmBzcpNfqaFeG7ExJ3m5i3CCbbmJU1OKtBne5IXx
sS1kGdRyxZjJjPOOrlxjXmgiJB1OZalgOB4KCCC6Pffx6qwGa67qHsqDVT+7LGNU
CsUHCLMKViiMfYAfVf79GXZNK8mnki8pPCXc50qCGre3LRq6Egmb8NIsSIj05aHM
UeQbOuOM+Bbf/dICYLV8qFmR2xpM3G5CmVX07LzGCX5k320z0kHrxH/r6QXl/bEP
X5kMRdoYfUoX6jDnd71aoLVDaPqZvDLDOMDG59riqcMsaWVqv7iZn2keWT6WTPfE
ZwGl4gECgYEA5GJlVXFcg91lSHWVprXeJHwIT4um8reGB6xt1CMxmhGx/e5vUiSo
KirrYEf4sDlE81MY0oLo0oZzDzadvTlPDFazacZZlNOVattOdC/L2TKzkfmsR18o
j1LsQApnDVVXGYLzGoQmASPk9GtfOE1phKZSyXZ3pV3D/JFQ1vHWmVkCgYEAwbJ0
ohW369yGSZUdV/vnpcpAmqav3duT1vx7UIUW7OUv5TTYmeXnpHV9m3Egdtsgy4Cy
eULrnKJqQ0Cnon4Lg/wzZPVKKdnBH94+duhSNu4+Q5DNFp9IEi76KFm7UI8vOX4e
4QtAIQUUBQjnjcW0fLlOw1r1Nkqcrwbw8dVMFFECgYAVTmCpwfOhkav7QIz/ioP4
32FfGmYuypREbv+oBMiB2RjD2dSk0yqlFG/1AYHf3tfh42SzbucNjOF7D9tTZd9M
BWKjgY+l5L9Rwrfk+viHgMVj3ukFl4kPJetIZjAK/GUtyhun46AwBws7CjFN7Vrk
tyeOB/FNihvYmi3yf4lHsQKBgQC1pntVClMq4ewaE7qqKbart4pwvoPN5z+1baDj
+Xxve9w38yBy67YaeIjsfuI4NPaDgtVdfVHi2joXignsDJMWGy3Dr3n2150TGuSv
tN5tX26LBMAhSA1Z6C54KvbM7QsXutyQpnFkxhNpSVmGjnPeSBbChInUeZKJXlQW
J7eqkQKBgDX88tAAM/FIZANoKPfmuoiFJ33USdC5mwNsHNBZvMAR2UsSeBSJZzy3
iar0ldCuTBolGpwRkLs1+pgoc4XDGDdV9367gjppQa0EqvrMwqNe8hcR7K/Dm+MU
B1lk88g8TJK7fUd6ibkJtmWTZXMGdCSC2+NG1mjeRbf1d2TB+zHM
-----END RSA PRIVATE KEY-----
";

        public const string JwtAlgorithm = "RS256";
        public const string Issuer = "Zonciu";
        public const int Lifttime = 86400;

        private TokenValidationParameters _tokenValidationParameters { get; set; } = new TokenValidationParameters()
        {
            ValidateActor = false,
            ValidateAudience = false,

            ValidateIssuer = true,
            ValidIssuer = "Zonciu",

            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new RsaSecurityKey(Rsa.CreateFromPublicKey(publicKey)),

            ValidateLifetime = true,
            RequireExpirationTime = true,
        };

        private SigningCredentials _signingCredentials { get; set; } =
            new SigningCredentials(new RsaSecurityKey(Rsa.CreateFromPrivateKey(privateKey)), JwtAlgorithm);

        private JwtSecurityTokenHandler _jwtSecurityTokenHandler { get; set; } = new JwtSecurityTokenHandler();

        /// <summary>
        /// 创建编码后的Jwt
        /// </summary>
        /// <param name="claimsIdentity">身份声明</param>
        /// <param name="jwtId">令牌Id</param>
        /// <returns></returns>
        public string CreateEncodedJwt(ClaimsIdentity claimsIdentity, string jwtId)
        {
            var jwtDesc = CreateSecurityTokenDescriptor(claimsIdentity, jwtId);
            return _jwtSecurityTokenHandler.CreateEncodedJwt(jwtDesc);
        }

        /// <summary>
        /// 创建Jwt
        /// </summary>
        /// <param name="descriptor"></param>
        /// <returns></returns>
        public JwtSecurityToken CreateJwt(SecurityTokenDescriptor descriptor)
        {
            return _jwtSecurityTokenHandler.CreateJwtSecurityToken(descriptor);
        }

        /// <summary>
        /// 创建Jwt描述
        /// </summary>
        /// <param name="claimsIdentity">身份声明</param>
        /// <param name="jwtId">令牌Id</param>
        /// <returns></returns>
        public SecurityTokenDescriptor CreateSecurityTokenDescriptor(ClaimsIdentity claimsIdentity, string jwtId)
        {
            claimsIdentity.AddClaim(new Claim("jti", jwtId));
            var issueTime = DateTime.Now;
            var jwtDesc = new SecurityTokenDescriptor
            {
                Issuer = Issuer,
                IssuedAt = issueTime,
                Expires = issueTime + TimeSpan.FromSeconds(Lifttime),
                SigningCredentials = _signingCredentials,
                Subject = claimsIdentity
            };
            return jwtDesc;
        }

        /// <summary>
        /// 校验Jwt
        /// </summary>
        /// <param name="jwtToken"></param>
        /// <param name="securityToken"></param>
        /// <returns></returns>
        public ClaimsPrincipal ValidateJwtToken(string jwtToken, out SecurityToken securityToken)
        {
            return _jwtSecurityTokenHandler.ValidateToken(jwtToken, _tokenValidationParameters, out securityToken);
        }
    }

ValidateJwtToken中校验失败直接抛出异常,校验成功则返回ClaimsPrincipal(Controller里HttpContext.User这货),并out出从string解析到的SecurityToken(Jwt反序列化的意思)。在NET Core 1.1里不用主动调用验证方法,这里只是用来做测试或者其他用途。

测试:

 public static void JwtTest()
        {
            var jwtFactory = new JwtFactory();

            var claims = new List<Claim>()
            {
                new Claim("role", "myRole"),
                new Claim("org", "myOrg")
            };
            var jti = Guid.NewGuid().ToString("N");
            var jwtString = jwtFactory.CreateEncodedJwt(new ClaimsIdentity(claims), jti);
            var jwt = jwtFactory.CreateJwt(jwtFactory.CreateSecurityTokenDescriptor(new ClaimsIdentity(claims), jti));
            var claimsPrincipal = jwtFactory.ValidateJwtToken(jwtString, out var token);
            var jwtClaims = claimsPrincipal.Claims.Select(
                claim => new
                {
                    claim.Type,
                    claim.Value
                }).ToList();
            Console.WriteLine(
                $@"
playloadClaims: {jwtClaims.ToJsonString(camelCase: false, indented: true)}

jwtString: {jwtString}

token.ToJsonString: {token.ToJsonString(camelCase: false, indented: true)}

jwt: {jwt}.{jwt.RawSignature}

");
        }

输出结果:

playloadClaims: [
  {
    "Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
    "Value": "myRole"
  },
  {
    "Type": "org",
    "Value": "myOrg"
  },
  {
    "Type": "jti",
    "Value": "a8b8ea421e834fd1b90ac09dbf40e158"
  },
  {
    "Type": "nbf",
    "Value": "1498397026"
  },
  {
    "Type": "exp",
    "Value": "1498483426"
  },
  {
    "Type": "iat",
    "Value": "1498397026"
  },
  {
    "Type": "iss",
    "Value": "Zonciu"
  }
]

jwtString: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoibXlSb2xlIiwib3JnIjoibXlPcmciLCJqdGkiOiJhOGI4ZWE0MjFlODM0ZmQxYjkwYWMwOWRiZjQwZTE1OCIsIm5iZiI6MTQ5ODM5NzAyNiwiZXhwIjoxNDk4NDgzNDI2LCJpYXQiOjE0OTgzOTcwMjYsImlzcyI6IlpvbmNpdSJ9.F5g9XanhffrPGwlvve5YA7fxU9-ENnunkqxTuRKE5rTWo2fthxs_MHXew44LJ21MJOxeWgS6j5Asws9VAjHGjOuxclFxhuf4jTE9otk4w8JEKf8IHhERJy4qKO1ooNUM3wp-WGzreJNCWRNxax2eT4EiCJZsawUBZtl-r4yztNMUGea37wgnta4CyzZTzNErzDeaAZLMb96YYdOjKSKP5Od5Hm-W0tqWaRhyzOTQ9nqHW7AV_g7qffUBQGUwqdL8H4dsDxzelS2xY5Ypjr-a2Z6hByYPPwyiBXkwXJYO9wKCSVuG72Y54UG_6R-dbowPmTwvirsnyeqWjQ2dFY0l9Q

token.ToJsonString: {
  "Actor": null,
  "Audiences": [],
  "Claims": [
    {
      "Issuer": "Zonciu",
      "OriginalIssuer": "Zonciu",
      "Properties": {},
      "Subject": null,
      "Type": "role",
      "Value": "myRole",
      "ValueType": "http://www.w3.org/2001/XMLSchema#string"
    },
    {
      "Issuer": "Zonciu",
      "OriginalIssuer": "Zonciu",
      "Properties": {},
      "Subject": null,
      "Type": "org",
      "Value": "myOrg",
      "ValueType": "http://www.w3.org/2001/XMLSchema#string"
    },
    {
      "Issuer": "Zonciu",
      "OriginalIssuer": "Zonciu",
      "Properties": {},
      "Subject": null,
      "Type": "jti",
      "Value": "a8b8ea421e834fd1b90ac09dbf40e158",
      "ValueType": "http://www.w3.org/2001/XMLSchema#string"
    },
    {
      "Issuer": "Zonciu",
      "OriginalIssuer": "Zonciu",
      "Properties": {},
      "Subject": null,
      "Type": "nbf",
      "Value": "1498397026",
      "ValueType": "http://www.w3.org/2001/XMLSchema#integer"
    },
    {
      "Issuer": "Zonciu",
      "OriginalIssuer": "Zonciu",
      "Properties": {},
      "Subject": null,
      "Type": "exp",
      "Value": "1498483426",
      "ValueType": "http://www.w3.org/2001/XMLSchema#integer"
    },
    {
      "Issuer": "Zonciu",
      "OriginalIssuer": "Zonciu",
      "Properties": {},
      "Subject": null,
      "Type": "iat",
      "Value": "1498397026",
      "ValueType": "http://www.w3.org/2001/XMLSchema#integer"
    },
    {
      "Issuer": "Zonciu",
      "OriginalIssuer": "Zonciu",
      "Properties": {},
      "Subject": null,
      "Type": "iss",
      "Value": "Zonciu",
      "ValueType": "http://www.w3.org/2001/XMLSchema#string"
    }
  ],
  "EncodedHeader": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9",
  "EncodedPayload": "eyJyb2xlIjoibXlSb2xlIiwib3JnIjoibXlPcmciLCJqdGkiOiJhOGI4ZWE0MjFlODM0ZmQxYjkwYWMwOWRiZjQwZTE1OCIsIm5iZiI6MTQ5ODM5NzAyNiwiZXhwIjoxNDk4NDgzNDI2LCJpYXQiOjE0OTgzOTcwMjYsImlzcyI6IlpvbmNpdSJ9",
  "Header": {
    "alg": "RS256",
    "typ": "JWT"
  },
  "Id": "a8b8ea421e834fd1b90ac09dbf40e158",
  "Issuer": "Zonciu",
  "Payload": {
    "role": "myRole",
    "org": "myOrg",
    "jti": "a8b8ea421e834fd1b90ac09dbf40e158",
    "nbf": 1498397026,
    "exp": 1498483426,
    "iat": 1498397026,
    "iss": "Zonciu"
  },
  "InnerToken": null,
  "RawAuthenticationTag": null,
  "RawCiphertext": null,
  "RawData": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoibXlSb2xlIiwib3JnIjoibXlPcmciLCJqdGkiOiJhOGI4ZWE0MjFlODM0ZmQxYjkwYWMwOWRiZjQwZTE1OCIsIm5iZiI6MTQ5ODM5NzAyNiwiZXhwIjoxNDk4NDgzNDI2LCJpYXQiOjE0OTgzOTcwMjYsImlzcyI6IlpvbmNpdSJ9.F5g9XanhffrPGwlvve5YA7fxU9-ENnunkqxTuRKE5rTWo2fthxs_MHXew44LJ21MJOxeWgS6j5Asws9VAjHGjOuxclFxhuf4jTE9otk4w8JEKf8IHhERJy4qKO1ooNUM3wp-WGzreJNCWRNxax2eT4EiCJZsawUBZtl-r4yztNMUGea37wgnta4CyzZTzNErzDeaAZLMb96YYdOjKSKP5Od5Hm-W0tqWaRhyzOTQ9nqHW7AV_g7qffUBQGUwqdL8H4dsDxzelS2xY5Ypjr-a2Z6hByYPPwyiBXkwXJYO9wKCSVuG72Y54UG_6R-dbowPmTwvirsnyeqWjQ2dFY0l9Q",
  "RawEncryptedKey": null,
  "RawInitializationVector": null,
  "RawHeader": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9",
  "RawPayload": "eyJyb2xlIjoibXlSb2xlIiwib3JnIjoibXlPcmciLCJqdGkiOiJhOGI4ZWE0MjFlODM0ZmQxYjkwYWMwOWRiZjQwZTE1OCIsIm5iZiI6MTQ5ODM5NzAyNiwiZXhwIjoxNDk4NDgzNDI2LCJpYXQiOjE0OTgzOTcwMjYsImlzcyI6IlpvbmNpdSJ9",
  "RawSignature": "F5g9XanhffrPGwlvve5YA7fxU9-ENnunkqxTuRKE5rTWo2fthxs_MHXew44LJ21MJOxeWgS6j5Asws9VAjHGjOuxclFxhuf4jTE9otk4w8JEKf8IHhERJy4qKO1ooNUM3wp-WGzreJNCWRNxax2eT4EiCJZsawUBZtl-r4yztNMUGea37wgnta4CyzZTzNErzDeaAZLMb96YYdOjKSKP5Od5Hm-W0tqWaRhyzOTQ9nqHW7AV_g7qffUBQGUwqdL8H4dsDxzelS2xY5Ypjr-a2Z6hByYPPwyiBXkwXJYO9wKCSVuG72Y54UG_6R-dbowPmTwvirsnyeqWjQ2dFY0l9Q",
  "SecurityKey": null,
  "SignatureAlgorithm": "RS256",
  "SigningCredentials": null,
  "EncryptingCredentials": null,
  "SigningKey": {
    "HasPrivateKey": false,
    "KeySize": 2048,
    "Parameters": {
      "D": null,
      "DP": null,
      "DQ": null,
      "Exponent": null,
      "InverseQ": null,
      "Modulus": null,
      "P": null,
      "Q": null
    },
    "Rsa": {
      "LegalKeySizes": [
        {
          "MinSize": 512,
          "MaxSize": 16384,
          "SkipSize": 64
        }
      ],
      "KeySize": 2048
    },
    "KeyId": null,
    "CryptoProviderFactory": {
      "CustomCryptoProvider": null
    }
  },
  "Subject": null,
  "ValidFrom": "2017-06-25T13:23:46Z",
  "ValidTo": "2017-06-26T13:23:46Z"
}

jwt: {"alg":"RS256","typ":"JWT"}.{"role":"myRole","org":"myOrg","jti":"a8b8ea421e834fd1b90ac09dbf40e158","nbf":1498397026,"exp":1498483426,"iat":1498397026,"iss":"Zonciu"}.F5g9XanhffrPGwlvve5YA7fxU9-ENnunkqxTuRKE5rTWo2fthxs_MHXew44LJ21MJOxeWgS6j5Asws9VAjHGjOuxclFxhuf4jTE9otk4w8JEKf8IHhERJy4qKO1ooNUM3wp-WGzreJNCWRNxax2eT4EiCJZsawUBZtl-r4yztNMUGea37wgnta4CyzZTzNErzDeaAZLMb96YYdOjKSKP5Od5Hm-W0tqWaRhyzOTQ9nqHW7AV_g7qffUBQGUwqdL8H4dsDxzelS2xY5Ypjr-a2Z6hByYPPwyiBXkwXJYO9wKCSVuG72Y54UG_6R-dbowPmTwvirsnyeqWjQ2dFY0l9Q

公钥和密钥是openssl生成的2048位key

openssl genrsa -out rsa_private_key.pem 2048
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

把jwtString和公钥、密钥拿去https://jwt.io/验证成功

Jwt在NET Core 1.1的引入方式:

在Startup的Configure方法中加入这个(JwtOptions和JwtEventsDefaults是我自己写的类)

因为Jwt校验失败的原因有很多种,校验失败时触发OnAuthenticationFailed事件,这个部分可以自己实现,也可以不管,默认会在Response的Header里添加错误信息。

 app.UseJwtBearerAuthentication(
                new JwtBearerOptions
                {
                    RequireHttpsMetadata = jwtFactory.JwtOptions.EnableHttps,
                    AutomaticAuthenticate = true,
                    AutomaticChallenge = true,
                    Events = new JwtBearerEvents()
                    {
                        OnAuthenticationFailed = JwtEventsDefaults.AuthenticationFailed
                    },
                    ClaimsIssuer = jwtFactory.JwtOptions.Issuer,
                    TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateAudience = false,
                        ValidateActor = false,
                        ValidateIssuer = true,
                        ValidIssuer = jwtFactory.JwtOptions.Issuer,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new RsaSecurityKey(jwtFactory.JwtOptions.PublicRsa),
                        RequireExpirationTime = true,
                    }
                });

有一个比较头疼的地方就是role这个claim,在jwt里是“role”没有变,解析之后会变成“http://schemas.microsoft.com/ws/2008/06/identity/claims/role”,这里要小心。(话说谁有办法解决这个问题吗?虽说改个名字就能避免被转换,但是好别扭也好憋屈……)

Jwt的删除办法

在我的实现中是添加了“jti”这个键,即Jwt Id,当服务端需要使Jwt提前失效,只能通过stateful的方式处理(因为你没办法保证客户端那边真的删掉了这个Jwt,比如证件你只能登报声明作废,但是如果其他单位没有检查作废信息,别人拿着你的证件去搞事情一样会通过),即在服务端把这个jti加入黑名单,黑名单删除时间是Jwt有效期之后的时间。这样的话就可以使得Jwt失效前通过黑名单来完成拒绝,黑名单清除之后通过Jwt的exp来完成拒绝。

时间: 2024-12-19 11:07:01

NET Core 1.1中使用Jwt的相关文章

aspnet core 2.1中使用jwt从原理到精通一

目录 原理: 根据原理使用C#语言,生成jwt: 自定义验证jwt: 使用aspnetcore 中自带的类生成jwt: 学有所得 了解jwt原理; 使用C#轻松实现jwt生成和验证 原理 jwt对所有语言都是通用的,只要知道秘钥,另一一种语言有可以对jwt的有效性进行判断; jwt的组成:Header部分Base64转化.Payload部分Base64转化.使用HS256方式根据秘钥对前面两部分进行加密后再Base64转化,其中使用的hs256加密是header部分指定的,也可以通过官网的查看,

ASP.NET Core 2.2 : 二十七. JWT与用户授权(细化到Action)

上一章分享了如何在ASP.NET Core中应用JWT进行用户认证以及Token的刷新,本章继续进行下一步,用户授权.涉及到的例子也以上一章的为基础.(ASP.NET Core 系列目录) 一.概述 首先说一下认证(authentication)与授权(authorization),它们经常在一起工作,所以有时候会分不清楚.并且这两个英文单词长得也像兄弟.举例来说,我刷门禁卡进入公司,门禁[认证]了我是这里的员工,可以进入:但进入公司以后,我并不是所有房间都可以进,比如“机房重地,闲人免进”,我

避免在ASP.NET Core 3.0中为启动类注入服务

本篇是如何升级到ASP.NET Core 3.0系列文章的第二篇. Part 1 - 将.NET Standard 2.0类库转换为.NET Core 3.0类库 Part 2 - IHostingEnvironment VS IHostEnvironent - .NET Core 3.0中的废弃类型 Part 3 - 避免在ASP.NET Core 3.0中为启动类注入服务(本篇) Part 4 - 将终端中间件转换为ASP.NET Core 3.0中的端点路由 Part 5 - 将集成测试的

在ASP.NET Core 2.0中使用CookieAuthentication

在ASP.NET Core中关于Security有两个容易混淆的概念一个是Authentication(认证),一个是Authorization(授权).而前者是确定用户是谁的过程,后者是围绕着他们允许做什么,今天的主题就是关于在ASP.NET Core 2.0中如何使用CookieAuthentication认证. 在ASP.NET Core 2.0中使用CookieAuthentication跟在1.0中有些不同,需要在ConfigureServices和Configure中分别设置,前者我

说说ASP.Net Core 2.0中的Razor Page

随着.net core2.0的发布,我们可以创建2.0的web应用了.2.0中新东西的出现,会让我们忘记老的东西,他就是Razor Page.下面的这篇博客将会介绍ASP.Net Core 2.0中的Razor Page. 在ASP.Net Core 2.0新特点之一就是支持Razor Page.今天的Razor Page是ASP.Net Core MVC中的一个子集.ASP.Net Core MVC 支持Razor Page意味着Razor Page应用从技术上来说就是MVC应用,同时Razo

用ASP.NET Core 1.0中实现邮件发送功能

准备将一些项目迁移到 asp.net core 先从封装类库入手,在遇到邮件发送类时发现在 asp.net core 1.0中并示提供SMTP相关类库,于是网上一搜发现了MailKit 好东西一定要试一下,何况是开源,下面是代码可实现SMTP邮件发送: using MailKit.Net.Smtp; using MailKit.Security; using MimeKit; using System.Threading.Tasks; namespace ConsoleApp1 { public

ASP.NET Core 1.0 中使用 Swagger 生成文档

github:https://github.com/domaindrivendev/Ahoy 之前文章有介绍在ASP.NET WebAPI 中使用Swagger生成文档,ASP.NET Core 1.0中同样也支持. 依赖包 "dependencies": { "Swashbuckle.SwaggerGen": "6.0.0-rc1-final", "Swashbuckle.SwaggerUi": "6.0.0-rc

正在开发的一个以最简化的操作在 .NET Core 1.0 中处理微信请求的SDK

现有的微信SDK库在 .NET Core 1.0 中表示很有压力,比如 Deepleo.Weixin.SDK 和 Senparc.Weixin(即使现有一个转移到.NET Core 1.0的初始项目,但是暂时还不能用) .前者轻量后者全而轻便,但是按照我的个人观点看来,前者太轻便了,对于不记参数的我来说点不出来就等于白搭,后者需要自定义一个处理方法来执行相关任务,但是相关的类型对于我这个就爱点出来能解决的事绝不干多余的是的我来说,还是不大乐意去用.因此就结合两者自行开发一个适合自己使用的SDK库

在ASP.NET Core Web API中为RESTful服务增加对HAL的支持

HAL(Hypertext Application Language,超文本应用语言)是一种RESTful API的数据格式风格,为RESTful API的设计提供了接口规范,同时也降低了客户端与服务端接口的耦合度.很多当今流行的RESTful API开发框架,包括Spring REST,也都默认支持HAL规范,当RESTful API被调用后,服务端就会返回ContentType为application/hal+json的JSON内容,例如: { "_links": { "