Spring Security Oauth2 JWT实战入门及使用

1、JWT研究

1.1、简介

JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公钥/私钥对来签名,防止被篡改。

JWT令牌的优点:
1、jwt基于json,非常方便解析。
2、可以在令牌中自定义丰富的内容,易扩展。
3、通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
4、资源服务使用JWT可不依赖认证服务即可完成授权。
缺点:
1、JWT令牌较长,占存储空间比较大。

1.2令牌结构

WT令牌由三部分组成,每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz

  • Header头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA)
  • Payload第二部分是负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的现成字段,比如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。
  • 第三部分是签名,此部分用于防止jwt内容被篡改。这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明签名算法进行签名。

1.3JWT入门

Spring Security 提供对JWT的支持,本节我们使用Spring Security 提供的JwtHelper来创建JWT令牌,校验JWT令牌等操作。

2、生成公钥及私钥

2.1生成密钥证书

随便创建一个文件夹,打开cmd命令,输入如下命令即可创建:

keytool -genkeypair -alias xckey -keyalg RSA -keypass (密码) -keystore xc.keystore -storepass (密码库)

  • Keytool 是一个java提供的证书管理工具
  • -alias:密钥的别名
  • -keyalg:使用的hash算法
  • -keypass:密钥的访问密码
  • -keystore:密钥库文件名,xc.keystore保存了生成的证书
  • -storepass:密钥库的访问密码

填写一堆信息后,将生成一个.keystore文件,可通过命令keytool -list -keystore xc.keystore 查看证书信息(会要求输入创建时使用的密码库校验)

2.2导出公钥

这里需要使用到openssl(一个加解密工具包),安装 openssl:http://slproweb.com/products/Win32OpenSSL.html,安装后配置到系统环境变量,在密钥证书目录下,执行如下命令:

keytool -list -rfc --keystore xc.keystore | openssl x509 -inform pem -pubkey

把公钥这一段复制到文本(注意调整为一行,不要有换行),即可放入项目资源服务中使用(resoureces)。

2.3测试生成jwt令牌

Spring boot创建测试类,测试jwt令牌的生成与验证:

引入Maven:

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
 </dependency>
 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
 </dependency>
//生成一个jwt令牌
@Test
public void testCreateJwt(){
    //证书文件
    String key_location = "xc.keystore";
    //密钥库密码
    String keystore_password = "xuechengkeystore";
    //访问证书路径
    ClassPathResource resource = new ClassPathResource(key_location);
    //密钥工厂
    KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource,
    keystore_password.toCharArray());
    //密钥的密码,此密码和别名要匹配
    String keypassword = "xuecheng";
    //密钥别名
    String alias = "xckey";
    //密钥对(密钥和公钥)
    KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias,keypassword.toCharArray());
    //私钥
    RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate();
    //定义payload信息
    Map<String, Object> tokenMap = new HashMap<>();
    tokenMap.put("id", "123");
    tokenMap.put("name", "mrt");
    tokenMap.put("roles", "r01,r02");
    tokenMap.put("ext", "1");
    //生成jwt令牌
    Jwt jwt = JwtHelper.encode(JSON.toJSONString(tokenMap), new RsaSigner(aPrivate));
    //取出jwt令牌
    String token = jwt.getEncoded();
    System.out.println("token="+token);
}

2.4验证jwt令牌

//资源服务使用公钥验证jwt的合法性,并对jwt解码
@Test
public void testVerify(){
	//jwt令牌
	String token ="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHQiOiIxIiwicm9sZXMiOiJyMDEscjAyIiwibmFtZSI6Im1ydCIsImlkIjoiMTIzIn0.KK7_67N5d1Dthd1PgDHMsbi0UlmjGRcm_XJUUwseJ2eZyJJWoPP2IcEZgAU3tUaaKEHUf9wSRwaDgwhrwfyIcSHbs8oy3zOQEL8j5AOjzBBs7vnRmB7DbSaQD7eJiQVJOXO1QpdmEFgjhc_IBCVTJCVWgZw60IEW1_Lg5tqaLvCiIl26K48pJB5f‐le2zgYMzqR1L2LyTFkq39rG57VOqqSCi3dapsZQd4ctq95SJCXgGdrUDWtD52rp5o6_0uq‐mrbRdRxkrQfsa1j8C5IW2‐T4eUmiN3f9wF9JxUK1__XC1OQkOn‐ZTBCdqwWIygDFbU7sf6KzfHJTm5vfjp6NIA";
	//公钥
	String publickey = "‐‐‐‐‐BEGIN PUBLIC KEY‐‐‐‐‐MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAijyxMdq4S6L1Af1rtB8SjCZHNgsQG8JTfGy55eYvzG0B/E4AudR2prSRBvF7NYPL47scRCNPgLnvbQczBHbBug6uOr78qnWsYxHlW6Aa5dI5NsmOD4DLtSw8eX0hFyK5Fj6ScYOSFBz9cd1nNTvx2+oIv0lJDcpQdQhsfgsEr1ntvWterZt/8r7xNN83gHYuZ6TM5MYvjQNBc5qC7Krs9wM7UoQuL+s0X6RlOib7/mcLn/lFLsLDdYQAZkSDx/6+t+1oHdMarChIPYT1sx9Dwj2j2mvFNDTKKKKAq0cv14Vrhz67Vjmz2yMJePDqUi0JYS2r0iIo7n8vN7s83v5uOQIDAQAB‐‐‐‐‐END PUBLIC KEY‐‐‐‐‐";
	//校验jwt
	Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publickey));
	//获取jwt中自定义内容
	String claims = jwt.getClaims();
    System.out.println(claims);
	//jwt令牌
	String encoded = jwt.getEncoded();
	System.out.println(encoded);
}

3、Spring Security Oauth2校验令牌

3.1、授权码模式申请令牌

1、跳转auth登录页面

输入账号和密码,点击Login。Spring Security接收到请求会调用UserDetailsService接口的loadUserByUsername方法查询用户正确的密码(测试可先固定配置好账户密码,实际开发就数据库中查)。

2、进入授权页面获取授权码

登录成功之后会自动跳转到授权码页面

Get:  localhost:40400/auth/oauth/authorizeclient_id=XcWebApp&response_type=code&scop=app&redirect_uri=http://localhost

参数:

client_id:客户端id,和授权配置类(SecurityConfig.java)中设置的客户端id一致。
response_type:授权码模式固定为code
scop:客户端范围,和授权配置类中设置的scop一致。
redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)。

点击Authorize认证服务携带授权码跳转redirect_uri。

3、申请令牌

拿到授权码后,申请令牌:

Post请求:http://localhost:40400/auth/oauth/token

此链接需要使用 http Basic认证。

使用postman测试:

http basic认证(账户密码与申请授权码时一致的):

Post请求参数:

grant_type:授权类型,填写authorization_code,表示授权码模式
code:授权码,就是刚刚获取的授权码,注意:授权码只使用一次就无效了,需要重新申请。
redirect_uri:申请授权码时的跳转url,一定和申请授权码时用的redirect_uri一致。

access_token:访问令牌,携带此令牌访问资源
token_type:有MAC Token与Bearer Token两种类型,两种的校验算法不同,RFC 6750建议Oauth2采用 Bearer
Token(http://www.rfcreader.com/#rfc6750)。
refresh_token:刷新令牌,使用此令牌可以延长访问令牌的过期时间。
expires_in:过期时间,单位为秒。
scope:范围,与定义的客户端范围一致。

3.2、Oauth2密码模式申请令牌

密码模式(Resource Owner Password Credentials)与授权码模式的区别是申请令牌不再使用授权码,而是直接通过用户名和密码即可申请令牌。
Post请求:http://localhost:40400/auth/oauth/token
参数:
grant_type:密码模式授权填写password
username:账号
password:密码
并且此链接需要使用 http Basic认证。

上边参数使用x-www-form-urlencoded方式传输,使用postman测试如下:
 

注意:当令牌没有过期时同一个用户再次申请令牌则不再颁发新令牌。 

 3.3校验令牌

Spring Security Oauth2提供校验令牌的端点,如下:

Get: http://localhost:40400/auth/oauth/check_token?token=

参数:
token:令牌

exp:过期时间,long类型,距离1970年的秒数(new Date().getTime()可得到当前时间距离1970年的毫秒数)。
user_name: 用户名
client_id:客户端Id,在oauth_client_details中配置
scope:客户端范围,在oauth_client_details表中配置
jti:与令牌对应的唯一标识
companyId、userpic、name、utype、id:这些字段是本认证服务在Spring Security基础上扩展的用户身份信息 

3.4刷新令牌

刷新令牌是当令牌快过期时重新生成一个令牌,它于授权码授权和密码授权生成令牌不同,刷新令牌不需要授权码也不需要账号和密码,只需要一个刷新令牌、客户端id和客户端密码。
测试如下:
Post:http://localhost:40400/auth/oauth/token
参数:

grant_type: 固定为 refresh_token
refresh_token:刷新令牌(注意不是access_token,而是refresh_token)

刷新令牌成功,会重新生成新的访问令牌和刷新令牌,令牌的有效期也比旧令牌长。
刷新令牌通常是在令牌快过期时进行刷新 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值