大致的流程是:客户端去认证中心申请访问凭证token,然后认证中心对于客户端请求来的帐号密码进行验证,如果验证通过,则颁发token,返回给客户端,客户端拿着 token 去各个微服务请求数据接口,一般这个 token 是放到 header 中的。当微服务接到请求后,先要拿着 token 去认证服务端检查 token 的合法性,如果合法,再根据用户所属的角色及具有的权限动态的返回数据
首先要讲两个东西
client_id
是客户端标识符,用于唯一标识一个已注册的客户端应用程序。每个注册的客户端应该被分配一个唯一的client_id
client_id
**,以表明请求的是哪个客户端应用程序client_secret
**是客户端的秘密,它作为客户端的密码,用于对客户端的身份进行认证,确保只有受信任的客户端才能获取访问令牌使用细节:
POST http://localhost:6001/oauth/token?grant_type=password&username=admin&password=123456&scope=all
Accept: */*
Cache-Control: no-cache
Authorization: Basic dXNlci1jbGllbnQ6dXNlci1zZWNyZXQtODg4OA==
Authorization 要加在请求头中,格式为 Basic 空格 base64(clientId:clientSecret),这个微服务客户端的 client-id 是 user-client,client-secret 是 user-secret-8888,将这两个值通过冒号连接,并使用 base64 编码(user-client:user-secret-8888)之后的值dXNlci1jbGllbnQ6dXNlci1zZWNyZXQtODg4OA==,可以通过 www.sojson.com/base64.html 在线编码获取。可以自己设定
运行请求后,如果参数都正确的话,获取到的返回内容如下,是一段 json 格式
{
"access_token": "9f958300-5005-46ea-9061-323c9e6c7a4d",
"token_type": "bearer",
"refresh_token": "0f5871f5-98f1-405e-848e-80f641bab72e",
"expires_in": 3599,
"scope": "all"
}
access_token : 就是之后请求需要带上的 token,也是本次请求的主要目的 token_type:为 bearer,这是 access token 最常用的一种形式 refresh_token:之后可以用这个值来换取新的 token,而不用输入账号密码 expires_in:token 的过期时间(秒)
GET http://localhost:6101/client-user/get
Accept: */*
Cache-Control: no-cache
Authorization: bearer ce334918-e666-455a-8ecd-8bd680415d84
### 换取 access_token
POST http://localhost:6001/oauth/token?grant_type=refresh_token&refresh_token=706dac10-d48e-4795-8379-efe8307a2282
Accept: */*
Cache-Control: no-cache
Authorization: Basic dXNlci1jbGllbnQ6dXNlci1zZWNyZXQtODg4OA==
注意如果以角色名为检验粒度,角色名必须带有ROLE_前缀
如果是以权限名为校验粒度,可自行更改,在UserDetailsService
每一个资源服务器必须要有资源服务配置,以下仅供参考,指定jwt的加密字段,要与认证服务器相对应
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Value("${security.oauth2.client.client-id}")
private String clientId;
@Value("${security.oauth2.client.client-secret}")
private String secret;
@Value("${security.oauth2.authorization.check-token-access}")
private String checkTokenEndpointUrl;
@Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Resource
private TokenStore jwtTokenStore;
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("topview@666");
accessTokenConverter.setVerifierKey("topview@666");
return accessTokenConverter;
}
@Bean
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setClientId(clientId);
tokenService.setClientSecret(secret);
tokenService.setCheckTokenEndpointUrl(checkTokenEndpointUrl);
return tokenService;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(jwtTokenStore);
}
}
security:
oauth2:
client:
client-id: user-client
client-secret: user-secret-8888
user-authorization-uri: http://localhost:6001/oauth/authorize #授权码模式
access-token-uri: http://localhost:6001/oauth/token #密码模式获取token接口
resource:
jwt:
key-uri: http://localhost:6001/oauth/token_key
key-value: topview@666
authorization:
check-token-access: http://localhost:6001/oauth/check_token #拿着请求中的 token 到认证服务端做 token 验证
ResourceServerConfig
配置就行,路径填写在yml文件中,异常处理器和过滤器同理security:
url:
allowed:
- /user/testAllowed
need:
- /user/get
密码模式的使用场景通常是在已经获得用户的授权的情况下,用于客户端应用程序直接代表用户向授权服务器请求访问令牌,然后使用访问令牌来访问受保护的资源服务器。但需要注意的是,密码模式要求客户端应用程序直接处理用户的凭据(用户名和密码),这可能带来一些安全风险,因此在使用密码模式时应谨慎,并考虑其他更安全的授权模式,如授权码模式或隐式授权模式。
OAuth2中的访问令牌(Access Token)通常是与特定的客户端应用程序(使用client_id标识)和资源服务器(例如API服务)相关联的。这意味着通过密码模式或其他授权模式获得的访问令牌通常只能用于访问其对应的client_id所注册的服务。
当用户通过密码模式(或其他授权模式)向授权服务器验证身份并获取访问令牌时,授权服务器会将该令牌与相应的client_id关联,并根据授权服务器的配置,可能会限制该令牌仅对特定资源服务器或API服务有效。
如果用户想要访问其他client_id所对应的服务,通常需要重新获取相应client_id的访问令牌。用户可以通过重新认证(提供用户名和密码等凭据)或通过其他授权流程来获取新的访问令牌,该令牌将与目标client_id关联,从而允许访问目标服务。
这种行为是出于安全和权限控制的考虑,以确保客户端只能访问其被授权访问的资源,而不会访问其他未经授权的资源。 OAuth2为每个客户端应用程序提供了一种独立的身份验证和授权机制,以细粒度地管理不同客户端的访问权限。
总而言之,如果客户端想要请求一个服务的资源,该资源的client-id和client_secret必须要在auth认证服务中注册,同时在请求体中加上相应信息,详情见上