在本文中,我们将为您详细介绍基于Java验证jwttoken代码实例的相关知识,并且为您解答关于jwt校验token的疑问,此外,我们还会提供一些关于.netcore基于Jwt实现Token令牌、As
在本文中,我们将为您详细介绍基于Java验证jwt token代码实例的相关知识,并且为您解答关于jwt校验token的疑问,此外,我们还会提供一些关于.net core 基于Jwt实现Token令牌、Asp.Net Core 3.1 学习4、Web Api 中基于JWT的token验证及Swagger使用、ASP.NET Web API 2系列(四):基于JWT的token身份认证方案、asp.net – 如何使用JwtSecurityTokenHandler和JWKS端点验证JWT?的有用信息。
本文目录一览:- 基于Java验证jwt token代码实例(jwt校验token)
- .net core 基于Jwt实现Token令牌
- Asp.Net Core 3.1 学习4、Web Api 中基于JWT的token验证及Swagger使用
- ASP.NET Web API 2系列(四):基于JWT的token身份认证方案
- asp.net – 如何使用JwtSecurityTokenHandler和JWKS端点验证JWT?
基于Java验证jwt token代码实例(jwt校验token)
这篇文章主要介绍了基于Java验证jwt token代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
这篇文章主要介绍了基于Java验证jwt token代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
How to load public certificate from pem file..?地址
1.HS256对称加密
package jwt; import java.io.FileInputStream; import java.io.IOException; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.Date; import java.util.Vector; import java.util.Map; import sun.misc.BASE64Decoder; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; public class JWTValidator { private static String JWT_Type = "JWT"; protected boolean validated; protected Object[] claims; public JWTValidator() { setValidated(false); setClaims(null); } public String Generate(String secret, String issuer, String audience, String subject){ try { Algorithm algorithm = Algorithm.HMAC256(secret); // HS256 String token = JWT.create() .withIssuer(issuer) .withAudience(audience) .withSubject(subject) .sign(algorithm); System.out.println(token); return token; } catch (Exception exception){ //UTF-8 encoding not supported return ""; } } public void Validate(String token, String secret, String issuer, String audience, String subject) { DecodedJWT jwt = null; setValidated(false); if (token == null || secret == null || issuer == null || audience == null || subject == null) return; try { jwt = JWT.require(Algorithm.HMAC256(secret.getBytes())).build().verify(token); } catch (JWTVerificationException e) { return; } if (jwt == null || jwt.getType() == null || !jwt.getType().contentEquals(JWT_Type)) return; if (!jwt.getIssuer().contentEquals(issuer) || !jwt.getAudience().contains(audience) || !jwt.getSubject().contentEquals(subject)) return; Date Now = new Date(); if ((jwt.getNotBefore() != null && jwt.getNotBefore().after(Now)) || (jwt.getExpiresAt() != null && jwt.getExpiresAt().before(Now))) return; setValidated(true); Map claimsMap = jwt.getClaims(); Vector claimsVector = new Vector(); if (claimsMap != null) { for (Map.Entry entry : claimsMap.entrySet()) { String key = entry.getKey(); if (key != null && !key.matches("aud|sub|iss|exp|iat")) { //claimsVector.add(new Claim(key, entry.getValue().asstring())); } } } setClaims(claimsVector.isEmpty() ? null : claimsVector.toArray()); } public boolean isValidated() { return validated; } public void setValidated(boolean val) { validated = val; } public Object[] getClaims() { return claims; } public void setClaims(Object[] val) { claims = (val == null ? new Object[0] : val); } }
2.RS256不对称加密,需要用public cert来验证
package jwt; import junit.framework.TestCase; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IoUtils; import org.jose4j.jws.AlgorithmIdentifiers; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.JwtClaims; import org.jose4j.jwt.consumer.JwtConsumer; import org.jose4j.jwt.consumer.JwtConsumerBuilder; import org.jose4j.lang.JoseException; import sun.security.util.DerInputStream; import sun.security.util.DerValue; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.math.BigInteger; import java.security.*; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.X509EncodedKeySpec; import java.text.SimpleDateFormat; import java.util.UUID; public class JWTValidatorForRSA extends TestCase{ public void testCreatetoken() throws IOException { System.out.println(createtoken()); } public void testVerifyToken() throws Exception { String token = createtoken(); System.out.println(token); String pkeyPath = "D:\temp\idsrv4.crt"; JwtClaims jwtClaims = verifyToken(token,pkeyPath); System.out.println(jwtClaims.getClaimValue("name")); System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jwtClaims.getIssuedAt().getValueInMillis())); System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jwtClaims.getExpirationTime().getValueInMillis())); } /** * 生成jwt,SHA256加密 * @return * @throws IOException */ public String createtoken() throws IOException { String privateKeyPath = "D:\temp\idsrv4.key"; PrivateKey privateKey = getPrivateKey(getStringFromFile(privateKeyPath)); final JwtClaims claims = new JwtClaims(); claims.setClaim("name", "jack"); claims.setSubject("a@a.com"); claims.setAudience("test");//用于验证签名是否合法,验证方必须包含这些内容才验证通过 claims.setExpirationTimeMinutesInTheFuture(-1); // 60*24*30); claims.setIssuedAtToNow(); // Generate the payload final JsonWebSignature jws = new JsonWebSignature(); jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); jws.setPayload(claims.toJson()); jws.setKeyIdHeaderValue(UUID.randomUUID().toString()); // Sign using the private key jws.setKey(privateKey); try { return jws.getCompactSerialization(); } catch (JoseException e) { return null; } } /** * 验证jwt * @param token * @return * @throws Exception */ public JwtClaims verifyToken(String token,String publicKeyPath) throws Exception { try { PublicKey publicKey = getPublicKey(publicKeyPath); JwtConsumer jwtConsumer = new JwtConsumerBuilder() .setRequireExpirationTime() .setVerificationKey(publicKey) .setExpectedAudience("test")//用于验证签名是否合法,可以设置多个,且可设置必须存在项,如果jwt中不包含这些内容则不通过 .build(); return jwtConsumer.processtoClaims(token); } catch (Exception e) { throw new RuntimeException(e); } } private String getStringFromFile(String filePath) throws IOException { // 生成方法:安装openssl,执行 openssl genrsa -out private.pem 2048 return IoUtils.toString(new FileInputStream(filePath)); } /** * 获取PublicKey对象 * @param publicKeyBase64 * @return * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws CertificateException * @throws FileNotFoundException */ private PublicKey getPublicKey(String publicKeyPath) throws NoSuchAlgorithmException, InvalidKeySpecException, CertificateException, FileNotFoundException { /* Not work : data isn't an object ID (tag = 2) String pem = publicKeyBase64 .replaceAll("\-*BEGIN.*CERTIFICATE\-*", "") .replaceAll("\-*END.*CERTIFICATE\-*", ""); java.security.Security.addProvider( new org.bouncycastle.jce.provider.BouncyCastleProvider() ); System.out.println(pem); X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(pem)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(pubKeySpec); */ CertificateFactory fact = CertificateFactory.getInstance("X.509"); FileInputStream is = new FileInputStream (publicKeyPath); X509Certificate cer = (X509Certificate) fact.generateCertificate(is); PublicKey publicKey = cer.getPublicKey(); System.out.println(publicKey); return publicKey; } /** * 获取PrivateKey对象 * @param privateKeyBase64 * @return */ private PrivateKey getPrivateKey(String privateKeyBase64) { String privKeyPEM = privateKeyBase64 .replaceAll("\-*BEGIN.*KEY\-*", "") .replaceAll("\-*END.*KEY\-*", ""); // Base64 decode the data byte[] encoded = Base64.decodeBase64(privKeyPEM); try { DerInputStream derReader = new DerInputStream(encoded); DerValue[] seq = derReader.getSequence(0); if (seq.length
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小编。
.net core 基于Jwt实现Token令牌
Startup类ConfigureServices中
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,//validate the server
ValidateAudience = true,//ensure that the recipient of the token is authorized to receive it
ValidateLifetime = true,//check that the token is not expired and that the signing key of the issuer is valid
ValidateIssuerSigningKey = true,//verify that the key used to sign the incoming token is part of a list of trusted keys
ValidIssuer = Configuration["Jwt:Issuer"],//appsettings.json文件中定义的Issuer
ValidAudience = Configuration["Jwt:Issuer"],//appsettings.json文件中定义的Audience
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};//appsettings.json文件中定义的JWT Key
});
Configure 启用中间件
app.UseAuthentication();//配置授权
appsetting.json中配置
"Jwt": {
"Key": "veryVerySecretKey",
"Issuer": "http://localhost:65356"
}
Api控制器中 根据登录信息生成token令牌
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;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using OnlineClassroom.Common;
using OnlineClassroom.Entity;
using OnlineClassroom.IService;
namespace OnlineClassroom.Api.Controllers
{
[Authorize]
[Route("api/[controller]/[action]")]
[ApiController]
public class UsersApiController : ControllerBase
{
private IConfiguration _config;
public IUsersService iUsersService = null;
public UsersApiController(IConfiguration config, IUsersService _iUsersService)
{
_config = config;
iUsersService = _iUsersService;
}/// <summary>
/// 登录
/// </summary>
/// <param name="Name">用户名</param>
/// <param name="Pwd">密码</param>
/// <returns>自定义结果</returns>
[HttpPost, AllowAnonymous]
public IActionResult Login(string Name, string Pwd)
{
IActionResult response = Unauthorized();
LoginModel login = new LoginModel();
login.Username = Name;
login.Password = Pwd;
var user = Authenticate(login);
if (user != null)
{
var tokenString = BuildToken(user);
response = Ok(new {User=user.user, token = tokenString});
}
return response;
}
/// <summary>
/// 根据用户信息生成token
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
private string BuildToken(UserModel user)
{
//添加Claims信息
var claims = new[] {
new Claim(JwtRegisteredClaimNames.Sub, user.Name),
new Claim(JwtRegisteredClaimNames.Email, user.Password),
new Claim(JwtRegisteredClaimNames.Birthdate, user.Birthdate.ToString("yyyy-MM-dd")),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Issuer"],
claims,//添加claims
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
//一个典型的JWT 字符串由三部分组成:
//header: 头部,meta信息和算法说明
//payload: 负荷(Claims), 可在其中放入自定义内容, 比如, 用户身份等
//signature: 签名, 数字签名, 用来保证前两者的有效性
//三者之间由.分隔, 由Base64编码.根据Bearer 认证规则, 添加在每一次http请求头的Authorization字段中, 这也是为什么每次这个字段都必须以Bearer jwy - token这样的格式的原因.
return new JwtSecurityTokenHandler().WriteToken(token);
}
private UserModel Authenticate(LoginModel login)
{
UserModel user = null;
var users = iUsersService.Login(login.Username, login.Password);
if (users != null)
{
user = new UserModel { Name = login.Username, Password = login.Password,user=users };
}
return user;
}
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
}
private class UserModel
{
public Users user { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public DateTime Birthdate { get; set; }
}
}
}
Asp.Net Core 3.1 学习4、Web Api 中基于JWT的token验证及Swagger使用
1、初始JWT
1.1、JWT原理
JWT(JSON Web Token)是目前最流行的跨域身份验证解决方案,他的优势就在于服务器不用存token便于分布式开发,给APP提供数据用于前后端分离的项目。登录产生的 token的项目完全可以独立与其他项目。当用户访问登录接口的时候会返回一个token,然后访问其他需要登录的接口都会带上这个token,后台进行验证如果token是有效的我们就认为用户是正常登录的,然后我们可以从token中取出来一些携带的信息进行操作。当然这些携带的信息都可以通过其他额外的字段进行传递,但是用token传递的话,不用其他额外加其他字段了。
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
1.2、JWT结构
JWT是由三段信息构成的,将这三段信息文本用.
链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbklEIjoiYWRtaW4iLCJuYmYiOjE1ODc4OTE2OTMsImV4cCI6MTU4NzkyNzY5MywiaXNzIjoiV1lZIiwiYXVkIjoiRXZlcnlUZXN0T25lIn0.-snenNVHrrKq9obN8FzKe0t99ok6FUm5pHv-P_eYc30
第一部分我们称它为头部(header):声明类型,这里是jwt;声明加密的算法 通常直接使用 HMAC SHA256
{
''typ'': ''JWT'',
''alg'': ''HS256''
}
第二部分我们称其为载荷(payload, 类似于飞机上承载的物品):
iss:Token发布者
exp:过期时间 分钟
sub:主题
aud:Token接受者
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT
除以上默认字段外,我们还可以自定义私有字段,如下例:
{
"sub": "1234567890",
"name": "wyy",
"admin": true
}
第三部分是签证(signature):这个部分需要base64加密后的header和base64加密后的payload使用.
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret
组合加密,然后就构成了jwt的第三部分。
2、生成Token
2.1、建立项目
在VS2019中新建一个Core Api程序 Core选3.1 然后在项目上添加一个Jwt文件夹帮助类,新建接口ITokenHelper,类:TokenHelper继承ITokenHelper,类JWTConfig,类TnToken
JWTConfig:用来保存读取jwt相关配置
/// <summary>
/// 配置token生成信息
/// </summary>
public class JWTConfig
{
/// <summary>
/// Token发布者
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// oken接受者
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 秘钥
/// </summary>
public string IssuerSigningKey { get; set; }
/// <summary>
/// 过期时间
/// </summary>
public int AccessTokenExpiresMinutes { get; set; }
}
TnToken:存放Token 跟过期时间的类
/// <summary>
/// 存放Token 跟过期时间的类
/// </summary>
public class TnToken
{
/// <summary>
/// token
/// </summary>
public string TokenStr { get; set; }
/// <summary>
/// 过期时间
/// </summary>
public DateTime Expires { get; set; }
}
ITokenHelper接口:token工具类的接口,方便使用依赖注入,很简单提供两个常用的方法
/// <summary>
/// token工具类的接口,方便使用依赖注入,很简单提供两个常用的方法
/// </summary>
public interface ITokenHelper
{
/// <summary>
/// 根据一个对象通过反射提供负载生成token
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="user"></param>
/// <returns></returns>
TnToken CreateToken<T>(T user) where T : class;
/// <summary>
/// 根据键值对提供负载生成token
/// </summary>
/// <param name="keyValuePairs"></param>
/// <returns></returns>
TnToken CreateToken(Dictionary<string, string> keyValuePairs);
}
TokenHelper:实现类
/// <summary>
/// Token生成类
/// </summary>
public class TokenHelper : ITokenHelper
{
private readonly IOptions<JWTConfig> _options;
public TokenHelper(IOptions<JWTConfig> options)
{
_options = options;
}
/// <summary>
/// 根据一个对象通过反射提供负载生成token
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="user"></param>
/// <returns></returns>
public TnToken CreateToken<T>(T user) where T : class
{
//携带的负载部分,类似一个键值对
List<Claim> claims = new List<Claim>();
//这里我们用反射把model数据提供给它
foreach (var item in user.GetType().GetProperties())
{
object obj = item.GetValue(user);
string value = "";
if (obj != null)
value = obj.ToString();
claims.Add(new Claim(item.Name, value));
}
//创建token
return CreateToken(claims);
}
/// <summary>
/// 根据键值对提供负载生成token
/// </summary>
/// <param name="keyValuePairs"></param>
/// <returns></returns>
public TnToken CreateToken(Dictionary<string, string> keyValuePairs)
{
//携带的负载部分,类似一个键值对
List<Claim> claims = new List<Claim>();
//这里我们通过键值对把数据提供给它
foreach (var item in keyValuePairs)
{
claims.Add(new Claim(item.Key, item.Value));
}
//创建token
return CreateTokenString(claims);
}
/// <summary>
/// 生成token
/// </summary>
/// <param name="claims">List的 Claim对象</param>
/// <returns></returns>
private TnToken CreateTokenString(List<Claim> claims)
{
var now = DateTime.Now;
var expires = now.Add(TimeSpan.FromMinutes(_options.Value.AccessTokenExpiresMinutes));
var token = new JwtSecurityToken(
issuer: _options.Value.Issuer,//Token发布者
audience: _options.Value.Audience,//Token接受者
claims: claims,//携带的负载
notBefore: now,//当前时间token生成时间
expires: expires,//过期时间
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey)), SecurityAlgorithms.HmacSha256));
return new TnToken { TokenStr = new JwtSecurityTokenHandler().WriteToken(token), Expires = expires };
}
}
2.2、在Startup中去配置jwt相关:
ConfigureServices中:
#region jwt配置
services.AddTransient<ITokenHelper, TokenHelper>();
//读取配置文件配置的jwt相关配置
services.Configure<JWTConfig>(Configuration.GetSection("JWTConfig"));
//启用JWT
services.AddAuthentication(Options =>
{
Options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
Options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).
AddJwtBearer();#endregion
JwtBearerDefaults.AuthenticationScheme与AddJwtBearer();下载两个依赖即可。或者NuGet安装
appsettings中简单配置一下jwt相关的信息:
"JWTConfig": {
"Issuer": "WYY", //Token发布者
"Audience": "EveryTestOne", //Token接受者
"IssuerSigningKey": "WYY&YL889455200Sily", //秘钥可以构建服务器认可的token;签名秘钥长度最少16
"AccessTokenExpiresMinutes": "600" //过期时间 分钟
},
Configure中去启用验证中间件:
//启用认证中间件 要写在授权UseAuthorization()的前面
app.UseAuthentication();
2.3、一个简单的登录获取token
在Controllers文件夹里面新建一个api 名字LoginTest
[EnableCors("AllowCors")]
[Route("api/[controller]/[action]")]
[ApiController]
public class LoginTestController : ControllerBase
{
private readonly ITokenHelper tokenHelper = null;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="_tokenHelper"></param>
public LoginTestController(ITokenHelper _tokenHelper)
{
tokenHelper = _tokenHelper;
}
/// <summary>
/// 登录测试
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost]
public ReturnModel Login([FromBody]UserDto user)
{
var ret = new ReturnModel();
try
{
if (string.IsNullOrWhiteSpace(user.LoginID) || string.IsNullOrWhiteSpace(user.Password))
{
ret.Code = 201;
ret.Msg = "用户名密码不能为空";
return ret;
}
//登录操作 我就没写了 || 假设登录成功
if (1 == 1)
{
Dictionary<string, string> keyValuePairs = new Dictionary<string, string>
{
{ "loginID", user.LoginID }
};
ret.Code = 200;
ret.Msg = "登录成功";
ret.TnToken= tokenHelper.CreateToken(keyValuePairs);
}
}
catch(Exception ex)
{
ret.Code = 500;
ret.Msg = "登录失败:"+ex.Message;
}
return ret;
}
}
UserDto接收类
/// <summary>
/// 登录类Dto
/// </summary>
public class UserDto
{
/// <summary>
/// 用户名
/// </summary>
public string LoginID { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
}
ReturnModel 只是我自己封装的一个统一的接口返回格式标准
/// <summary>
/// 返回类
/// </summary>
public class ReturnModel
{
/// <summary>
/// 返回码
/// </summary>
public int Code { get; set; }
/// <summary>
/// 消息
/// </summary>
public string Msg { get; set; }
/// <summary>
/// 数据
/// </summary>
public object Data { get; set; }
/// <summary>
/// Token信息
/// </summary>
public TnToken TnToken { get; set; }
}
跨域上篇文章说了这里就不提了
2.4、前端获取token
我是用传统的MVC的一个启动页面
<input type="hidden" id="tokenValue" name="tokenValue" value="" />
<br /><br /><br />
<span>Token:</span><div id="txtval"></div><br />
<span>有效期:</span><div id="txtvalTime"></div><br />
<div>
<input type="button" value="获取Token" onclick="getToken()" /><br /><br /><br />
</div>
<script src="~/Scripts/jquery-3.3.1.js"></script>
<script type="text/javascript">
//获取token
function getToken() {
var data = JSON.stringify({ LoginID: "admin", Password: "admin888" });
$.ajax({
type: "post",
url: "https://localhost:44331/api/LoginTest/Login",
dataType: "json",
async: true,
data: data,
contentType: ''application/json'',
success: function (data) {
console.log(data);
$("#txtval").html(data.tnToken.tokenStr);
$("#txtvalTime").html(new Date(data.tnToken.expires).Format("yyyy-MM-dd hh:mm"));
$("#tokenValue").val(data.tnToken.tokenStr);
},
error: function (data) {
console.log("错误" + data);
}
});
}
Date.prototype.Format = function (fmt) { //author: zhengsh 2016-9-5
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
</script>
把Api启动起来 MVC也启动起来试试看
在JWT管网解码
3、验证前端传递的token
现在说说怎么来验证前台传递的jwt,其实很简单,最主要的就是验证token的有效性和是否过期。在接口ITokenHelper中添加验证的两个方法 。TokenHelper中实现
ITokenHelper中添加
/// <summary>
/// Token验证
/// </summary>
/// <param name="encodeJwt">token</param>
/// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值</param>
/// <returns></returns>
bool ValiToken(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad = null);
/// <summary>
/// 带返回状态的Token验证
/// </summary>
/// <param name="encodeJwt">token</param>
/// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值</param>
/// <param name="action"></param>
/// <returns></returns>
TokenType ValiTokenState(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad, Action<Dictionary<string, string>> action);
TokenHelper中添加
/// <summary>
/// 验证身份 验证签名的有效性
/// </summary>
/// <param name="encodeJwt"></param>
/// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值, </param>
public bool ValiToken(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad = null)
{
var success = true;
var jwtArr = encodeJwt.Split(''.'');
if (jwtArr.Length < 3)//数据格式都不对直接pass
{
return false;
}
var header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0]));
var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));
//配置文件中取出来的签名秘钥
var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
//验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
if (!success)
{
return success;//签名不正确直接返回
}
//其次验证是否在有效期内(也应该必须)
var now = ToUnixEpochDate(DateTime.UtcNow);
success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));
//不需要自定义验证不传或者传递null即可
if (validatePayLoad == null)
return true;
//再其次 进行自定义的验证
success = success && validatePayLoad(payLoad);
return success;
}
/// <summary>
/// 时间转换
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
private long ToUnixEpochDate(DateTime date)
{
return (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
}
/// <summary>
///
/// </summary>
/// <param name="encodeJwt"></param>
/// <param name="validatePayLoad"></param>
/// <param name="action"></param>
/// <returns></returns>
public TokenType ValiTokenState(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad, Action<Dictionary<string, string>> action)
{
var jwtArr = encodeJwt.Split(''.'');
if (jwtArr.Length < 3)//数据格式都不对直接pass
{
return TokenType.Fail;
}
var header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0]));
var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));
var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
//验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
if (!string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1]))))))
{
return TokenType.Fail;
}
//其次验证是否在有效期内(必须验证)
var now = ToUnixEpochDate(DateTime.UtcNow);
if (!(now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())))
{
return TokenType.Expired;
}
//不需要自定义验证不传或者传递null即可
if (validatePayLoad == null)
{
action(payLoad);
return TokenType.Ok;
}
//再其次 进行自定义的验证
if (!validatePayLoad(payLoad))
{
return TokenType.Fail;
}
//可能需要获取jwt摘要里边的数据,封装一下方便使用
action(payLoad);
return TokenType.Ok;
}
其中TokenType是返回类型成功失败
public enum TokenType
{
Ok,
Fail,
Expired
}
在api LoginTest中新增两个验证的方法
/// <summary>
/// 验证Token
/// </summary>
/// <param name="tokenStr">token</param>
/// <returns></returns>
[HttpGet]
public ReturnModel ValiToken(string tokenStr)
{
var ret = new ReturnModel
{
TnToken = new TnToken()
};
bool isvilidate = tokenHelper.ValiToken(tokenStr);
if(isvilidate)
{
ret.Code = 200;
ret.Msg = "Token验证成功";
ret.TnToken.TokenStr = tokenStr;
}
else
{
ret.Code = 500;
ret.Msg = "Token验证失败";
ret.TnToken.TokenStr = tokenStr;
}
return ret;
}
/// <summary>
/// 验证Token 带返回状态
/// </summary>
/// <param name="tokenStr"></param>
/// <returns></returns>
[HttpGet]
public ReturnModel ValiTokenState(string tokenStr)
{
var ret = new ReturnModel
{
TnToken = new TnToken()
};
string loginID = "";
TokenType tokenType = tokenHelper.ValiTokenState(tokenStr, a => a["iss"] == "WYY" && a["aud"] == "EveryTestOne", action => { loginID = action["loginID"]; });
if (tokenType == TokenType.Fail)
{
ret.Code = 202;
ret.Msg = "token验证失败";
return ret;
}
if (tokenType == TokenType.Expired)
{
ret.Code = 205;
ret.Msg = "token已经过期";
return ret;
}
//..............其他逻辑
var data = new List<Dictionary<string, string>>();
var bb = new Dictionary<string, string>
{
{ "Wyy", "123456" }
};
data.Add(bb);
ret.Code = 200;
ret.Msg = "访问成功!";
ret.Data =data ;
return ret;
}
上面一个简单的验证和支持自定义验证的就写好了。下面带有状态的是让我们清楚的知道是什么状态请求登录的时候 或者请求数据的时候,是token过期还是说token没有获取到等等。
ValiTokenState第三个参数我还更了一个系统委托,是这样想的,处理可以验证token,还可以顺便取一个想要的数据,当然其实这样把相关逻辑混到一起也增加代码的耦合性,当时可以提高一点效率不用在重新解析一次数据,当然这个数据也可以通前台传递过来,所以怎么用还是看实际情况,这里只是封装一下提供这样一个方法,用的时候也可以用。
其前端请求代码
$.ajax({
type: "post",
url: "https://localhost:44331/api/LoginTest/ValiToken?tokenStr="+ $("#tokenValue").val(),
dataType: "json",
async: true,
data: { token: $("#tokenValue").val() },
contentType: ''application/json'',
success: function (data) {
console.log(data);
},
error: function (data) {
console.log("错误" + data);
}
});
4、Api中过滤器实现通用token验证
项目上新建一个文件夹Filter,在文件夹Filter里新建一个过滤器TokenFilter
namespace JWTToken.Filter
{
public class TokenFilter : Attribute, IActionFilter
{
private ITokenHelper tokenHelper;
public TokenFilter(ITokenHelper _tokenHelper) //通过依赖注入得到数据访问层实例
{
tokenHelper = _tokenHelper;
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
ReturnModel ret = new ReturnModel();
//获取token
object tokenobj = context.ActionArguments["token"];
if (tokenobj == null)
{
ret.Code = 201;
ret.Msg = "token不能为空";
context.Result = new JsonResult(ret);
return;
}
string token = tokenobj.ToString();
string userId = "";
//验证jwt,同时取出来jwt里边的用户ID
TokenType tokenType = tokenHelper.ValiTokenState(token, a => a["iss"] == "WYY" && a["aud"] == "EveryTestOne", action => { userId = action["userId"]; });
if (tokenType == TokenType.Fail)
{
ret.Code = 202;
ret.Msg = "token验证失败";
context.Result = new JsonResult(ret);
return;
}
if (tokenType == TokenType.Expired)
{
ret.Code = 205;
ret.Msg = "token已经过期";
context.Result = new JsonResult(ret);
}
if (!string.IsNullOrEmpty(userId))
{
//给控制器传递参数(需要什么参数其实可以做成可以配置的,在过滤器里边加字段即可)
//context.ActionArguments.Add("userId", Convert.ToInt32(userId));
}
}
}
}
context.ActionArguments。这是前段请求的时候地址栏带上的参数 token=xxx;这种类型的,不是请求的参数 不然会报错;
把过滤器在startup中注入一下:
services.AddScoped<TokenFilter>();
需要验证token的地方,直接加上这个过滤器即可
前台试试 请求上图的GetList
<input type="hidden" id="tokenValue" name="tokenValue" value="" />
<br /><br /><br />
<span>Token:</span><div id="txtval"></div><br />
<span>有效期:</span><div id="txtvalTime"></div><br />
<div>
<input type="button" value="获取Token" onclick="getToken()" /><br /><br /><br />
</div>
<input type="button" value="获取List" onclick="getList()" /><br />
<script src="~/Scripts/jquery-3.3.1.js"></script>
<script type="text/javascript">
//获取token
function getToken() {
var data = JSON.stringify({ LoginID: "admin", Password: "admin888" });
$.ajax({
type: "post",
url: "https://localhost:44331/api/LoginTest/Login",
dataType: "json",
async: true,
data: data,
contentType: ''application/json'',
success: function (data) {
console.log(data);
$("#txtval").html(data.tnToken.tokenStr);
$("#txtvalTime").html(new Date(data.tnToken.expires).Format("yyyy-MM-dd hh:mm"));
$("#tokenValue").val(data.tnToken.tokenStr);
},
error: function (data) {
console.log("错误" + data);
}
});
}
//获取list
function getList() {
var data = JSON.stringify();
$.ajax({
type: "post",
url: "https://localhost:44331/api/Home/GetList?token="+ $("#tokenValue").val(),
dataType: "json",
async: true,
data: { token: $("#tokenValue").val() },
contentType: ''application/json'',
success: function (data) {
console.log(data);
$("#txtval").html(JSON.stringify(data));
},
error: function (data) {
console.log("错误" + data);
}
});
}
Date.prototype.Format = function (fmt) { //author: zhengsh 2016-9-5
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
</script>
现获取token 赋值在隐藏框里在请求
5、在Api中使用Swagger
5.1项目中添加Swagger的相关包
5.2ConfigureServices、Configure 中添加
#region Swagger
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "测试接口文档",
Description = "测试接口"
});
// 为 Swagger 设置xml文档注释路径
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
c.DocInclusionPredicate((docName, description) => true);
//添加对控制器的标签(描述)
c.DocumentFilter<ApplyTagDescriptions>();//显示类名
c.CustomSchemaIds(type => type.FullName);// 可以解决相同类名会报错的问题
//c.OperationFilter<AuthTokenHeaderParameter>();
});
#endregion
app.UseSwagger(c =>
{
c.RouteTemplate = "swagger/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Web App v1");
c.RoutePrefix = "doc";//设置根节点访问
//c.DocExpansion(DocExpansion.None);//折叠
c.DefaultModelsExpandDepth(-1);//不显示Schemas
});
5.3、项目属性修改
5.4、添加接口类的注释
看效果
6、总结
JWT个人的理解就是api配置文件的IssuerSigningKey作为秘钥来加密的,客户端登录后获取到token 地址栏请求传到后端 后端通过解码获取到IssuerSigningKey是否跟后台解析出来的一直来匹配。后端可以卸载锅炉器里面来接收这个token来验证从而限制能不能访问Api。前端可以自己封装一个请求把token穿进去的参数就可以避免每次输入Token,前端可以Session?
下了班写的仓促了 哈哈。欢迎补充。
ASP.NET Web API 2系列(四):基于JWT的token身份认证方案
没有保护的API接口任何人都可以访问,完全没有安全性可言,这时就需要控制对它的访问,也就是WebAPI的权限验证。本文介绍一种常用的验证方式:基于JWT的token身份认证方案,讲解了它的原理,以及通过代码实现其验证的整个过程。1.引言
通过前边的系列教程,我们可以掌握WebAPI的初步运用,但是此时的API接口任何人都可以访问,这显然不是我们想要的,这时就需要控制对它的访问,也就是WebAPI的权限验证。验证方式非常多,本文就重点介绍一种常用的验证方式:基于JWT的token身份认证方案。
2.前期回顾
Web API系列(一):初识API及手动搭建基本框架
Web API系列(二):灵活多样的路由配置
Web API系列(三):添加接口详细说明及测试
3.认识JWT
JWT是 JSON Web Token 的缩写,是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
3.1 JWT工作流程
这里我们通过一张图了解它的工作流程。
从上图中我们可以看出它是基于Token的身份认证,具体流程:客户端携带用户名和密码请求访问 - 服务器校验用户凭据 - 应用提供一个token给客户端 - 客户端存储token,并且在随后的每一次请求中都带着它 -服务器校验token并返回数据。
3.2JWT结构
JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
- Header:头部,它有token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)两部分组成;
- Payload:荷载,它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明;
- Signature:签名,目的是用来验证头部和载荷是否被非法篡改。
通过下图,我们可以直观的看到JWT的组成。
它本质上是一个独立的身份验证令牌,可以包含用户标识、用户角色和权限等信息,以及您可以存储任何其他信息(自包含)。任何人都可以轻松读取和解析,并使用密钥来验证真实性。
4.具体实现
上文介绍了JWT的原理,读者简单了解即可,这里我们通过具体代码来实现。
4.1安装JWT包
通过NuGget管理工具安装JWT包,如下图
4.2添加LoginRequest、AuthInfo和HttpResult三个实体类
在MyWebAPI.Entities中添加相应类
LoginRequest实体
public class LoginRequest{ public string UserId { get; set; } public string Password { get; set; }}
AuthInfo实体类
public class AuthInfo{ public string UserId { get; set; } public DateTime Expires { get; set; }}
HttpResul实体类
public class HttpResult{ public bool Success { get; set; } public dynamic Data { get; set; } public string Message { get; set; }}
4.3添加SystemController,并添加Login登录方法
具体代码如下:
[RoutePrefix("api/System")]public class SystemController : ApiController{ [HttpPost, Route("Login")] public HttpResult Login([FromBody] LoginRequest loginRequest) { if (loginRequest == null) return new HttpResult() { Success = false, Message = "登录信息为空!" }; #region 通过数据库判断登录信息是否正确(这里简化判断) if (loginRequest.UserId != "admin" || loginRequest.Password != "admin") { return new HttpResult() { Success = false, Message = "用户名和密码不正确!" }; } #endregion AuthInfo authInfo = new AuthInfo() { UserId = loginRequest.UserId, Expires = DateTime.Now.AddDays(1) }; const string secretKey = "matanzhang";//口令加密秘钥(应该写到配置文件中) byte[] key = Encoding.UTF8.GetBytes(secretKey); IJwtAlgorithm algorithm = new HMACSHA256Algorithm();//加密方式 IJsonSerializer serializer = new JsonNetSerializer();//序列化Json IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();//base64加解密 IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);//JWT编码 var token = encoder.Encode(authInfo, key);//生成令牌 return new HttpResult() { Success = true, Data = token,Message = "登录成功!"}; }}
4.4添加API过滤器ApiAuthorizeAttribute
具体代码如下:
public class ApiAuthorizeAttribute: AuthorizeAttribute{ protected override bool IsAuthorized(HttpActionContext actionContext) { try { var authHeader = from t in actionContext.Request.Headers where t.Key == "auth" select t.Value.FirstOrDefault(); var enumerable = authHeader as string[] ?? authHeader.ToArray(); string token = enumerable.FirstOrDefault(); if (string.IsNullOrEmpty(enumerable.FirstOrDefault())) return fals.........
asp.net – 如何使用JwtSecurityTokenHandler和JWKS端点验证JWT?
如果我能重建这种行为会很好,我想尽可能利用微软的JwtSecurityTokenHandler
实现.但是,我无法弄清楚如何利用通过IdentityServer的发现端点提供的JsonWebKeySet和JsonWebKey类型来提取密钥并执行验证.
JwtSecurityTokenHandler使用TokenValidationParameters
来验证JWT,这些参数需要一个或多个SecurityKey
对象的实例来执行验证.
ClaimsPrincipal ValidateJwt(string token,IdentityModel.Client.discoveryResponse discovery) { JwtSecurityToken jwt = new JwtSecurityToken(token); TokenValidationParameters validationParameters = new TokenValidationParameters { ValidateAudience = true,ValidateIssuer = true,RequireSignedTokens = true,Validissuer = "expected-issuer",ValidAudience = "expected-audience",IssuerSigningKeys = discovery.KeySet.Keys /* not quite */ }; JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); SecurityToken validatedToken; return handler.Validatetoken(jwt,validationParameters,out validatedToken); }
如何从JsonWebKeySet到IEnumerable< SecurityKey>执行必要的转换?以便验证可以发生?是否有另一种方法(除了OWIN中间件)也可以使用上面的discoveryResponse数据?
(遗憾的是,System.IdentityModel.Tokens.Jwt的文档不是最新的.)
解决方法
https://github.com/IdentityServer/IdentityServer4.Samples/blob/dev/Clients/src/MvcManual/Controllers/HomeController.cs#L81
它从JWK手动检索密钥并填充验证参数.
今天关于基于Java验证jwt token代码实例和jwt校验token的分享就到这里,希望大家有所收获,若想了解更多关于.net core 基于Jwt实现Token令牌、Asp.Net Core 3.1 学习4、Web Api 中基于JWT的token验证及Swagger使用、ASP.NET Web API 2系列(四):基于JWT的token身份认证方案、asp.net – 如何使用JwtSecurityTokenHandler和JWKS端点验证JWT?等相关知识,可以在本站进行查询。
本文标签: