官方书籍:https://datatracker.ietf.org/doc/html/rfc6749
我们把
我的目的是:
官方文档中表述的流程:
(A) 客户端向资源所有者请求授权。这
可以直接向资源所有者提出授权请求
(如图所示),或最好通过授权间接
服务器作为中介。
(B) 客户端收到授权许可,这是一个
代表资源所有者授权的凭证,
使用本文件中定义的四种授权类型之一表示
规范或使用扩展授权类型。这
授权授予类型取决于所使用的方法
客户端请求授权和支持的类型
授权服务器。
(C) 客户端通过身份验证请求访问令牌
授权服务器并提供授权许可。
(D) 授权服务器对客户端进行认证并验证
授权许可,如果有效,则颁发访问令牌。
(E) 客户端从资源中请求受保护的资源
服务器并通过提供访问令牌进行身份验证。
(F) 资源服务器验证访问令牌,如果有效,
服务于请求。
流程:
Oauth2是一个标准的开放的授权协议,应用程序可以根据自己的要求去使用Oauth2,本项目计划使用Oauth2实现如 下目标:
1、YxinMiracle系统访问第三方系统的资源
2、外部系统访问YxinMiracle系统的资源
3、YxinMiracle系统前端(客户端) 访问YxinMiracle系统微服务的资源
4、YxinMiracle系统微服务之间访问资源,例如:微服务A访问微服务B的资源,B访问A的资源
本项目采用 Spring security + Oauth2完成用户认证及用户授权,Spring security 是一个强大的和高度可定制的身份验证和访问控制框架,Spring security 框架集成了Oauth2协议,下图是项目认证架构图:
1、用户请求认证服务完成认证。
2、认证服务下发用户身份令牌,拥有身份令牌表示身份合法。
3、用户携带令牌请求资源服务,请求资源服务必先经过网关。
4、网关校验用户身份令牌的合法,不合法表示用户没有登录,如果合法则放行继续访问。
5、资源服务获取令牌,根据令牌完成授权。
6、资源服务完成授权则响应资源信息。
认证服务器的文件结构:
加入依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-oauth2artifactId>
dependency>
配置文件:
server:
port: 9001
spring:
application:
name: user-auth
redis:
host: 192.168.211.132
port: 6379
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.211.132:3306/database?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=UTC
username: root
password: 123456
main:
allow-bean-definition-overriding: true
eureka:
instance:
prefer-ip-address: true
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
auth:
ttl: 3600 #token存储到redis的过期时间
clientId: yxinmiracle
clientSecret: yxinmiracle
cookieDomain: localhost
cookieMaxAge: -1
jks:
name: yxinmiracle.jks
storepassword: yxinmiracle
keypassword: yxinmiracle
alias: yxinmiracle
Oauth2有以下授权模式:
1.授权码模式(Authorization Code)
2.隐式授权模式(Implicit)
3.密码模式(Resource Owner Password Credentials)
4.客户端模式(Client Credentials)
其中常用的有:
授权码模式,密码模式,流程图如图1::
此时我们可以启动认证服务器,访问连接:
Get请求:
http://localhost:9001/oauth/authorize?client_id=yxinmiracle&response_type=code&scop=app&redirect_uri=http://localhost
访问之后会出现:
这就需要我们输入认证服务器系统的账号,利用认证服务器系统的账号登录我们的yxinmiracle系统,输入账号:
登录后会进行提示:
意思是:您确定要将认证服务器中的账号认证在yxinmiracle系统吗?这就相当于你们利用qq在别的平台登录一样,这个时候我们点击确认,就会返回一个授权码给我们,根据这个授权码我们可以向认证服务器请求TOKEN:
点击确认后重定向到:
http://localhost/?code=lfiS4M
其中lfiS4M
就是我们的授权码,我们可以根据这个授权码去获取Token
使用PostMan发送请求:
其中From表单中的数据是这样的:
KEY | VALUE |
---|---|
grant_type | authorization_code |
code | lfiS4M(获取到的授权码) |
redirect_uri | http://localhost |
我们的yxinmiracle系统与我们的认证系统也需要有一个认证的联系,这个联系是需要用过请求头中的信息关联的,也就是PostMan中的:
该请求会化作为请求头中的Authorization
,如下图所示:
发送请求:
数据(实际上就是一个jwt):
Key | 解释 |
---|---|
access_token | 令牌 |
token_type | 校验方式 |
refresh_token | 刷新令牌 |
expires_in | 过期时间 |
scope | 使用范围 |
jti | 唯一id |
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjIwNjEyODg0ODcsInVzZXJfbmFtZSI6InpoYW5nc2FuIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiJiMmNmYjMwYS0yNGU4LTQ2YWMtOGQ4ZC1lN2ZlYjFmZWJjNGQiLCJjbGllbnRfaWQiOiJjaGFuZ2dvdSIsInNjb3BlIjpbImFwcCJdfQ.UpSbU6xPVadYzl2sgzp2s43air8A5cd2wp11l0_U9BWj8TEtpnrj8PfTD_dMF7Al_hw_-kmeXvl4IX3F9wguUMBYmKYikJna8r0KsMUtA3f0YqD6lE6RcUv3EXfHb1WodTPeHUGLn55FEKDUJkVOzi0fYancCElACn9rMs3wEqhCj0AAs8_2Ee0Fg4wRtF9ZGCXuXZxnBrA-IYeJF5v7dL6w4Aa8YzcYuOmjmT3xrKH3FDDWnZql5YLrTlKEbNsR0AVQgQQXOBClym50zxOh_qtEkqMBCZQCIdkcDqF8H7EdB46bx3GDI6rw_Xz_eLoMVt0qpzwx3KxYt7_ZhpQ",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ6aGFuZ3NhbiIsInNjb3BlIjpbImFwcCJdLCJhdGkiOiJiMmNmYjMwYS0yNGU4LTQ2YWMtOGQ4ZC1lN2ZlYjFmZWJjNGQiLCJleHAiOjIwNjEyODg0ODcsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iLCJST0xFX1VTRVIiXSwianRpIjoiYmQ0ZjJmOTMtNTA3ZC00NWQzLWFlOGQtZGVlMWY5M2ExMTFlIiwiY2xpZW50X2lkIjoiY2hhbmdnb3UifQ.IVdKYliRDRJPf6bMxrx0XYDBFU00oOAJLjLC99tbkJIcJ75wKZSMdJU9exLhiqZ22ZLVBaI2KiMdSCP4DRAYuqV7DFL31c3CTsNEOrWktRgbK_z_T4RP_JIoK3pNPWk5uxcZXM6x2rnMU_n6Lu3_CYBh5xr3DC9a5HXtkmmpKzf7S3lXzm4Hq5DJKlqcnhRLLF97IDrqPUkXvcAbErdh0kKKY_bB1MwMl-ascBU8SKBPEFn96-nY0u0Fd4i0kAtQlnQ2TJFSIsOE8GQaKswYaNNJHh2JlyegiI1ByfPEu-iIDpKJv4OxZsMAnh2HmmLeWJbjf-0AhFpyaFtEcPsIZg",
"expires_in": 431999999,
"scope": "app",
"jti": "b2cfb30a-24e8-46ac-8d8d-e7feb1febc4d"
}
Spring Security Oauth2提供校验令牌的端点,如下:
Get: http://localhost:9001/oauth/check_token?token=
参数:
token:令牌
通过postman发送请求:
结果为:
{
"user_name": "zhangsan",
"scope": [
"app"
],
"active": true,
"exp": 2061288487,
"authorities": [
"ROLE_ADMIN",
"ROLE_USER"
],
"jti": "b2cfb30a-24e8-46ac-8d8d-e7feb1febc4d",
"client_id": "yxinmiracle"
}
刷新令牌是当令牌快过期时重新生成一个令牌,它于授权码授权和密码授权生成令牌不同,刷新令牌不需要授权码 也不需要账号和密码,只需要一个刷新令牌、客户端id和客户端密码。
测试如下: Post:http://localhost:9001/oauth/token
参数:
grant_type: 固定为 refresh_token
refresh_token:刷新令牌(注意不是access_token,而是refresh_token)
(1)认证
密码模式(Resource Owner Password Credentials)与授权码模式的区别是申请令牌不再使用授权码,而是直接 通过用户名和密码即可申请令牌。
测试如下:
Post请求:http://localhost:9001/oauth/token
参数:
grant_type:密码模式授权填写password
username:账号
password:密码
并且此链接需要使用 http Basic认证。
(2)校验令牌
Spring Security Oauth2提供校验令牌的端点,如下:
Get: http://localhost:9001/oauth/check_token?token=
参数:
token:令牌
使用postman测试如下:
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基础上扩展的用户身份信息
(1)传统授权流程
资源服务器授权流程如上图,客户端先去授权服务器申请令牌,申请令牌后,携带令牌访问资源服务器,资源服务器访问授权服务校验令牌的合法性,授权服务会返回校验结果,如果校验成功会返回用户信息给资源服务器,资源服务器如果接收到的校验结果通过了,则返回资源给客户端。
传统授权方法的问题是用户每次请求资源服务,资源服务都需要携带令牌访问认证服务去校验令牌的合法性,并根 据令牌获取用户的相关信息,性能低下。
(2)公钥私钥授权流程
传统的授权模式性能低下,每次都需要请求授权服务校验令牌合法性,我们可以利用公钥私钥完成对令牌的加密,如果加密解密成功,则表示令牌合法,如果加密解密失败,则表示令牌无效不合法,合法则允许访问资源服务器的资源,解密失败,则不允许访问资源服务器资源。
上图的业务流程如下:
1、客户端请求认证服务申请令牌
2、认证服务生成令牌认证服务采用非对称加密算法,使用私钥生成令牌。
3、客户端携带令牌访问资源服务客户端在Http header 中添加: Authorization:Bearer 令牌。
4、资源服务请求认证服务校验令牌的有效性资源服务接收到令牌,使用公钥校验令牌的合法性。
5、令牌有效,资源服务向客户端响应资源信息
在对称加密的时代,加密和解密用的是同一个密钥,这个密钥既用于加密,又用于解密。这样做有一个明显的缺点,如果两个人之间传输文件,两个人都要知道密钥,如果是三个人呢,五个人呢?于是就产生了非对称加密,用一个密钥进行加密(公钥),用另一个密钥进行解密(私钥)。
张三有两把钥匙,一把是公钥,另一把是私钥。
张三把公钥送给他的朋友们----李四、王五、赵六----每人一把。
李四要给张三写一封保密的信。她写完后用张三的公钥加密,就可以达到保密的效果。
张三收信后,用私钥解密,就看到了信件内容。这里要强调的是,只要张三的私钥不泄露,这封信就是安全的,即使落在别人手里,也无法解密。
张三给李四回信,决定采用"数字签名"。他写完后先用Hash函数,生成信件的摘要(digest)。张三将这个签名,附在信件下面,一起发给李四。
李四收信后,取下数字签名,用张三的公钥解密,得到信件的摘要。由此证明,这封信确实是张三发出的。李四再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。
Spring Security 提供对JWT的支持,本节我们使用Spring Security 提供的JwtHelper来创建JWT令牌,校验JWT令牌 等操作。 这里JWT令牌我们采用非对称算法进行加密,所以我们要先生成公钥和私钥。
(1)生成密钥证书 下边命令生成密钥证书,采用RSA 算法每个证书包含公钥和私钥
创建一个文件夹,在该文件夹下执行如下命令行:
keytool -genkeypair -alias yxinmiracle -keyalg RSA -keypass yxinmiracle -keystore yxinmiracle.jks -storepass yxinmiracle
Keytool 是一个java提供的证书管理工具
-alias:密钥的别名
-keyalg:使用的hash算法
-keypass:密钥的访问密码
-keystore:密钥库文件名,xc.keystore保存了生成的证书
-storepass:密钥库的访问密码
使用如图:
之后文件夹中会出现:
(2)查询证书信息
keytool -list -keystore yxinmiracle.jks
(3)删除别名
keytool -delete -alias yxinmiracle -keystore yxinmiracle.jsk
openssl是一个加解密工具包,这里使用openssl来导出公钥信息。
下载地址:http://slproweb.com/products/Win32OpenSSL.html
选择:
安装完后进行环境变量的添加
cmd进入yxinmiracle.jks文件所在目录执行如下命令(如下命令在windows下执行,会把-变成中文方式,请将它改成英文的-):
keytool -list -rfc --keystore yxinmiracle.jks | openssl x509 -inform pem -pubkey
结果:
下面段内容是公钥
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlTaqLMvTUHgaAdffduem
niZ2hee/lKmZ32rDS/862fbLuX4LdHWjkZrrWV2MzFrIYeu3zRr7QxCQy/rtvDKk
f9OKd+K3d2dJ8ljjVwmOAVI48BlBoZU66Ugu8oSepXWWP7d5sJcpCUzfUJc3AlZE
tvpttG+YJjOp2dI4rb7AAK1YD+kw6wi47rmCneYhQUtIq3pCqe/Owudceq0JOH40
R3IvXfsv/YHi5tFENT+f+Q/KMlFVWedRzMyBR12FvzGXiwYlYjcNbb8wAp3UWyuy
HinrbTI90EwjawUKMsjEbirKfN0JSjIdpg5J5EEMUk0XVaqMZZ3RPiRwFOTXmnkv
6wIDAQAB
-----END PUBLIC KEY-----
创建令牌:
package com.yxinmiracle.token;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.util.HashMap;
import java.util.Map;
public class CreateJwtTest {
/***
* 创建令牌测试
*/
@Test
public void testCreateToken(){
//证书文件路径
String key_location="yxinmiracle.jks";
//秘钥库密码
String key_password="yxinmiracle";
//秘钥密码
String keypwd = "yxinmiracle";
//秘钥别名
String alias = "yxinmiracle";
//访问证书路径
ClassPathResource resource = new ClassPathResource(key_location);
//创建秘钥工厂
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource,key_password.toCharArray());
//读取秘钥对(公钥、私钥)
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias,keypwd.toCharArray());
//获取私钥
RSAPrivateKey rsaPrivate = (RSAPrivateKey) keyPair.getPrivate();
//定义Payload
Map<String, Object> tokenMap = new HashMap<>();
tokenMap.put("id", "1");
tokenMap.put("name", "YxinMiracle");
tokenMap.put("roles", "ROLE_VIP,ROLE_USER");
//生成Jwt令牌
Jwt jwt = JwtHelper.encode(JSON.toJSONString(tokenMap), new RsaSigner(rsaPrivate)); // 要用私钥来作为秘钥放到jwt中创造令牌
//取出令牌
String encoded = jwt.getEncoded();
System.out.println(encoded);
}
}
结果:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlcyI6IlJPTEVfVklQLFJPTEVfVVNFUiIsIm5hbWUiOiJZeGluTWlyYWNsZSIsImlkIjoiMSJ9.gXlwor6RS4MkFKtm9wFY9hbzYZmT-OthHZ6cCbWdr50KIOw_MESCDgVFkkL_4PFwBI8unIVYAD5JM_4Fkr3KmrkjObhCkyANe_lhfWHzvBZWUpK2Wsc2B2NF3juePeEUHTGbSIwVbQng0CKNn_3vCK6IIxRMrZUBcqqBbtt6cKawtMK_CzOX3JpI3nqmAf7_5oq3aTKM0l0Lp1AsHiHZiJ_Umk9lqg86KxeDhrJjuS5cQffRGrPNQhs-0HrgqtBsyqvqBp8XHo8J82Sc_rdABF4R1Ynoye4tKDTg6TCnWcFgD3MkyNs5caHy0uch3bHjuYsV44JrnjEUCvWH85GVUA
解析令牌:
package com.yxinmiracle.token;
import org.junit.Test;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
public class ParseJwtTest {
/***
* 校验令牌
*/
@Test
public void testParseToken(){
//令牌
String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlcyI6IlJPTEVfVklQLFJPTEVfVVNFUiIsIm5hbWUiOiJZeGluTWlyYWNsZSIsImlkIjoiMSJ9.gXlwor6RS4MkFKtm9wFY9hbzYZmT-OthHZ6cCbWdr50KIOw_MESCDgVFkkL_4PFwBI8unIVYAD5JM_4Fkr3KmrkjObhCkyANe_lhfWHzvBZWUpK2Wsc2B2NF3juePeEUHTGbSIwVbQng0CKNn_3vCK6IIxRMrZUBcqqBbtt6cKawtMK_CzOX3JpI3nqmAf7_5oq3aTKM0l0Lp1AsHiHZiJ_Umk9lqg86KxeDhrJjuS5cQffRGrPNQhs-0HrgqtBsyqvqBp8XHo8J82Sc_rdABF4R1Ynoye4tKDTg6TCnWcFgD3MkyNs5caHy0uch3bHjuYsV44JrnjEUCvWH85GVUA";
//公钥
String publickey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlTaqLMvTUHgaAdffduemniZ2hee/lKmZ32rDS/862fbLuX4LdHWjkZrrWV2MzFrIYeu3zRr7QxCQy/rtvDKkf9OKd+K3d2dJ8ljjVwmOAVI48BlBoZU66Ugu8oSepXWWP7d5sJcpCUzfUJc3AlZEtvpttG+YJjOp2dI4rb7AAK1YD+kw6wi47rmCneYhQUtIq3pCqe/Owudceq0JOH40R3IvXfsv/YHi5tFENT+f+Q/KMlFVWedRzMyBR12FvzGXiwYlYjcNbb8wAp3UWyuyHinrbTI90EwjawUKMsjEbirKfN0JSjIdpg5J5EEMUk0XVaqMZZ3RPiRwFOTXmnkv6wIDAQAB-----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);
}
}
结果:
当我们访问/oauth/token
生成的令牌是通过这个方法生成的:
/**
*
* 创建令牌的方法
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
//设置jwt的转换器 必须要有
JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); // 使用jwt的方式创建令牌
//设置jwt的秘钥库
//秘钥库的名称
String keystorepath =jksProperties.getName(); // 从配置文件中读取配置
//秘钥库的密码
String storepassword = jksProperties.getStorepassword();
//读取秘钥对的密码
String keypassword = jksProperties.getKeypassword();
//秘钥库的别名
String alias = jksProperties.getAlias();
KeyPair keyPair = new KeyStoreKeyFactory(
new ClassPathResource(keystorepath), //设置加密的加载文件
storepassword.toCharArray())//设置读取秘钥库文件的密码
.getKeyPair(alias, keypassword.toCharArray());//设置获取秘钥的密码
//设置秘钥对象
converter.setKeyPair(keyPair);
//使用JWT的令牌转换器
DefaultAccessTokenConverter accessTokenConverter = (DefaultAccessTokenConverter) converter.getAccessTokenConverter();
return converter;
}
用户访问其他功能微服务时,必须判断该用户有没有登录,如果有并且写到的token是正确的(正不正确可以使用该功能微服务中的公钥与用户请求携带来的token进行校验,如果说校验成功那么就是登录过的)
首先我们要登录的话就需要访问我们的user微服务
在user微服务中添加一个配置类,并且把公钥放在user微服务的类路径下:
项目结构代码:
配置类代码:
package com.yxinmiracle.user.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Base64;
import java.util.stream.Collectors;
/**
* @version 1.0
* @author: YxinMiracle
* @date: 2021-08-17 16:41
*/
@Configuration
@EnableResourceServer // 标识这是一台资源服务器
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)//激活方法上的PreAuthorize注解
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
//公钥
private static final String PUBLIC_KEY = "public.key";
/***
* 定义JwtTokenStore
* @param jwtAccessTokenConverter
* @return
*/
@Bean
public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
return new JwtTokenStore(jwtAccessTokenConverter);
}
/***
* 定义JJwtAccessTokenConverter 转化器 既能创建令牌 也能解析令牌 用来配置公钥
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey(getPubKey()); // setVerifierKey需要的是一个字符串信息 getPubKey()得到文件中的公钥
return converter;
}
/**
* 获取非对称加密公钥 Key
* @return 公钥 Key
*/
private String getPubKey() {
Resource resource = new ClassPathResource(PUBLIC_KEY);
try {
InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());
BufferedReader br = new BufferedReader(inputStreamReader);
return br.lines().collect(Collectors.joining("\n"));
} catch (IOException ioe) {
return null;
}
}
/***
* Http安全配置,对每个到达系统的http请求链接进行校验
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
//所有请求必须认证通过
http.authorizeRequests()
//下边的路径放行
.antMatchers(
"/user/add","/user/load/{id}"). //配置地址放行 用户注册是需要放行的
permitAll()
.anyRequest().
authenticated(); //其他地址需要认证授权
}
}
此时在没有携带令牌的令牌的情况下进行访问(此时没有开启网关),可以发现没有权限:
此时我们可以进行登录:
然后携带令牌进行访问,但是在访问的时候需要在请求头中添加上Authorization
字段,key为bearer Token
,如:
再进行请求访问,发现请求成功:
小插曲
为什么是bearer呢?
因为在我们生成令牌的时候,可以发现有一个token类型,是需要bearer方式的。
经过网关的方式发送请求,发现网关并不能将用户的令牌携带到微服务去,如图报了401无权限的错误:
解决办法:
在网关内,我们需要将这个数据添加到请求头中,再传到下一个微服务中:
成功请求:
在user微服务中编写一个测试方法,只有角色有“ROLE_XXX”的人才能访问这个路径
可以发现在用户登录的时候我们都会给他配置权限:
在进行Token解析的时候也能看见用户拥有什么样的权限:
{
"user_name": "zhangsan",
"scope": [
"app"
],
"active": true,
"exp": 2061288487,
"authorities": [
"ROLE_ADMIN",
"ROLE_USER"
],
"jti": "b2cfb30a-24e8-46ac-8d8d-e7feb1febc4d",
"client_id": "yxinmiracle"
}
进行访问,可以发现并不能访问:
将权限设置为ROLE_ADMIN后:
进行访问,成功访问: