接着上一篇继续展示其他功能~
2.1: 如何拿着生成的access_token获取用户详情信息
有的小伙伴可能好奇这个jti是合适呢么东西,这个是jwt生成令牌的唯一标识,后期我们还要拿它来整合redis哦,因为他是唯一的,我们可以把它作为key~~~ 滑稽
这个是我们上一篇文章获取到了access_token,那么我现在想拿着当前的这个access_token去获取到我们登录用户的信息怎么办呢? 这个时候我们就要去调用OAuth2提供的另一个接口了,这个接口地址是: /oauth/check_token,页面展示如下:
别忘了是POST请求!!!
配置客户端id和密钥生成Basic的64编码
配置完请求头,可以看到由客户端id + 客户端密钥生成的base64编码
2.2:展示效果:
2.3: 如何在access_token中自定义一些参数返回
创建扩展器 TokenEnhancer,改扩展器可以让我们的请求信息中加入一些自定义信息,例如我现在在我的请求返回格式中加入一个userInfo的对象,返回的格式为:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzkxNjU0OTMsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiYXJ0aWNsZTpzZWFyY2giLCJ1c2VyOnBhc3N3b3JkIiwicm9sZSIsImFydGljbGU6YXVkaXQiLCJjYXRlZ29yeTpkZXRlbGUiLCJibG9nIiwiYWR2ZXJ0OmFkZCIsInJvbGU6YWRkIiwibGFiZWw6ZWRpdCIsInB1YmxpYyIsInVzZXI6c2VhcmNoIiwicm9sZTpkZWxldGUiLCJtZW51OmRlbGV0ZSIsImFkdmVydDpzZWFyY2giLCJhcnRpY2xlOnZpZXciLCJsYWJlbDphZGQiLCJyb2xlOnBlcm1pc3Npb24iLCJhZHZlcnQ6ZWRpdCIsInVzZXI6YWRkIiwidXNlcjpkZWxldGUiLCJjYXRlZ29yeTpzZWFyY2giLCJjYXRlZ29yeTplZGl0Iiwicm9sZTpzZWFyY2giLCJhZHZlciIsImluZGV4IiwibWVudTplZGl0IiwibGFiZWwiLCJtZW51IiwibWVudTpzZWFyY2giLCJhcnRpY2xlIiwiYWR2ZXJ0OmRlbGV0ZSIsIm1lbnU6YWRkIiwic3lzdGVtIiwibGFiZWw6ZGVsZXRlIiwiYXJ0aWNsZTpkZWxldGUiLCJ1c2VyOmVkaXQiLCJsYWJlbDpzZWFyY2giLCJ1c2VyOnJvbGUiLCJyb2xlOmVkaXQiLCJjYXRlZ29yeSIsInVzZXIiLCJjYXRlZ29yeTphZGQiXSwianRpIjoiOTNjOTBjZDItMmFhZC00M2Q5LTkwOTktZDNhYzhlZTZhN2MwIiwiY2xpZW50X2lkIjoibXhnLWJsb2ctYWRtaW4iLCJzY29wZSI6WyJhbGwiXX0.N8lNRdWYG2SJKEAXRW-BCDGUEI1ibi87pWOud7ZGSHDWNR-NBGSDuwL1_K2w-BBxlagLatTu2oTvHivKnA5kxoiTCPPZo9R7E9pEULHjJbttJ0PCGeOht6k-nOyQOKMDE9qjg56D9mnsc60cvp02E06ey9l_lBD50A2rOGUG9tthdvPY-OGJg96gdx4Em4h9aofP_MldVQJ3M-upvs4HNWaUo_MFWz5WH4FdABUldxN83CjG8vgct6eoIUxKZi10wadCyxBLYSD5-4HFYBvNBMNLZNnWZk7XCwYkL-R930Xxs4oZUUd76DDZpRutK_SUjTfGZq0QlJxhYE_50bcSRA",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiI5M2M5MGNkMi0yYWFkLTQzZDktOTA5OS1kM2FjOGVlNmE3YzAiLCJleHAiOjE2NDE3MTQyOTMsImF1dGhvcml0aWVzIjpbImFydGljbGU6c2VhcmNoIiwidXNlcjpwYXNzd29yZCIsInJvbGUiLCJhcnRpY2xlOmF1ZGl0IiwiY2F0ZWdvcnk6ZGV0ZWxlIiwiYmxvZyIsImFkdmVydDphZGQiLCJyb2xlOmFkZCIsImxhYmVsOmVkaXQiLCJwdWJsaWMiLCJ1c2VyOnNlYXJjaCIsInJvbGU6ZGVsZXRlIiwibWVudTpkZWxldGUiLCJhZHZlcnQ6c2VhcmNoIiwiYXJ0aWNsZTp2aWV3IiwibGFiZWw6YWRkIiwicm9sZTpwZXJtaXNzaW9uIiwiYWR2ZXJ0OmVkaXQiLCJ1c2VyOmFkZCIsInVzZXI6ZGVsZXRlIiwiY2F0ZWdvcnk6c2VhcmNoIiwiY2F0ZWdvcnk6ZWRpdCIsInJvbGU6c2VhcmNoIiwiYWR2ZXIiLCJpbmRleCIsIm1lbnU6ZWRpdCIsImxhYmVsIiwibWVudSIsIm1lbnU6c2VhcmNoIiwiYXJ0aWNsZSIsImFkdmVydDpkZWxldGUiLCJtZW51OmFkZCIsInN5c3RlbSIsImxhYmVsOmRlbGV0ZSIsImFydGljbGU6ZGVsZXRlIiwidXNlcjplZGl0IiwibGFiZWw6c2VhcmNoIiwidXNlcjpyb2xlIiwicm9sZTplZGl0IiwiY2F0ZWdvcnkiLCJ1c2VyIiwiY2F0ZWdvcnk6YWRkIl0sImp0aSI6ImM4N2RjOGRiLTRiZjAtNDVhMC1iNTRjLTdmZGNjMmQyZGYwMSIsImNsaWVudF9pZCI6Im14Zy1ibG9nLWFkbWluIn0.E8HS_ZjYc-5nSWvPkhDKQxHhJfcxLMFjLMpovZoRuM-GQ_9Nogs1AOCcM1bv_L4UjQx_Pv_JN_FA_OoIIW-bYtvCU3iqciQqpff_tyD2vu1zLbrP6cQDnaHhWC12zKjhi5jjPb01gxYvI9qomm_Zvh6VJvGPVxGP7oRWr3hEu0W7tmkNckDO_drs7632qMIXZbIVEys-knyak8fzZZLpp8RsVxi8M1CUq7cTDal48LkruwageQfZKDOoA1aZbgeNPI_bVbRY5B3zqBFNEnHQn1bloQUEarE7P2coETbD-A67XI2KJf0piTuJ39vuJvRKBAwmoum6YGH_qxCI0tG5Tw",
"expires_in": 43199,
"scope": "all",
"jti": "93c90cd2-2aad-43d9-9099-d3ac8ee6a7c0",
"userInfo":{
"。。。具体的用户信息或其他数据,这个userInfo为扩展的数据"
}
}
具体代码实现如下:
package com.qycq.oauth.security.config;
import com.alibaba.fastjson.JSON;
import com.qycq.oauth.pojo.JwtUser;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* <p>
* 自定义扩展器,当认证成功后,如果客户端需要其他用户信息,则可以进行扩展
* </p>
*
* @author 七月初七
* @version 1.0
* @date 2021/12/10 17:18
*/
@Configuration
public class JwtTokenEnhancer implements TokenEnhancer {
/**
* 对当前客户端提供自定义的用户数据返回,用于去做OAuth2身份认证
*
* OAuth2AccessToken: 它的实现类只有一个叫做 {@link DefaultOAuth2AccessToken} 中的 setAdditionalInformation()方法
* 他的方法主要是以下作用:
* 令牌授予者想要添加到令牌的附加信息,例如支持新的令牌类型。如果映射中的值是原始值
* 则远程通信将始终有效。使用地图(如果需要,嵌套)或由 Jackson 明确序列化的东西也应该是安全的。
*
* @param accessToken oauth2的token
* @param authentication authentication.getPrincipal()获取当前登录的用户信息
* @return
*/
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
/**
* 此处直接强转即可。因为我们知道当前的这个登录用户就是我们自定义的用户对象
*/
JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
Map<String, Object> map = new LinkedHashMap<>();
map.put("userInfo", JSON.toJSON(jwtUser));
/**
* 这样我们的accessToken就可以进行扩展信息了。然后将其配置到授权服务器中。{@link com.qycq.oauth.security.AuthorizationServerConfig}
*/
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(map);
return accessToken;
}
}
然后将其配置在授权服务器中
package com.qycq.oauth.security;
import com.qycq.oauth.security.config.ClientDetailsServiceConfig;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import java.util.ArrayList;
import java.util.List;
/**
* @author 七月初七
* @version 1.0
* @date 2021/12/10 14:25
*/
@EnableAuthorizationServer
@Configuration
@Slf4j
@ApiModel(value = "授权服务器")
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
/**
* 指定Bean的名称,因为这个clientDetailsService有一个地方已经用过了,不然就会报错
* 出现Bean名称重复导致项目起不来....
*/
@Autowired
@Qualifier("jdbcClientDetailsService")
private ClientDetailsService clientDetailsService;
/**
* 将{@link SpringSecurityConfig 中的 AuthenticationManager 注入}
*/
@Autowired
private AuthenticationManager authenticationManager;
/**
* 将我们自定义的登录逻辑注入进来,具体实现在 {@link com.qycq.oauth.service.impl.UserDetailsServiceImpl }
*/
@Autowired
private UserDetailsService userDetailsService;
/**
* JWT 方式存储令牌 具体配置在: {@link com.qycq.oauth.security.config.JwtTokenStoreConfig}
*/
@Autowired
private TokenStore tokenStore;
/**
* JWT 令牌转换器 具体配置在: {@link com.qycq.oauth.security.config.JwtTokenStoreConfig}
*/
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
/**
* 注入自定义扩展器
*/
@Autowired
private TokenEnhancer jwtTokenEnhancer;
/**
* 客户端详细信息服务配置
*
* <p>
* 可以选择在数据库中加载客户端信息,也可以使用内存的方式加载。
* 具体详见 {@link ClientDetailsServiceConfig}
* </p>
*
* @param clients 客户端信息
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
/**
* 授权访问端点
* <p>
* 该配置为OAuth2生成令牌的关键方法,此方法配置东西过多。
* </p>
* 1:我们需要去{@link SpringSecurityConfig} 中去配置 AuthenticationManager的Bean实例,
* 在SpringSecurity中必须覆盖此方法并加上@Bean注解,否则使用密码方式登录会有问题
*
* </br>
* <p>
* 2: 需要注入我们的UserDetailsService,让端点走我们自定义的登录逻辑
*
* </br>
* <p>
* 3: 配置哪种方式管理令牌,这边采用JWT方式存储令牌,将 TokenStore 和 JwtAccessTokenConverter 注入
* <p>
* 4: 具体配置参考 {@link com.qycq.oauth.security.config.JwtTokenStoreConfig}
* </p>
* <p>
* 5: 配置到此,即可访问生成accessToken了 访问: http: // 你的地址: 端口号: xxxx /oauth/token
* 这个接口地址是OAuth2自带的,你只需要去访问此接口将客户端id 和 密码 放入到请求头Basic认证的, 然后将用户名 密码 和grant_type 设置成password
* 然后使用POST 方式访问即可看到生成的accessToken令牌
* 具体参考 {@link org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration}
*
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.tokenStore(tokenStore)
.accessTokenConverter(jwtAccessTokenConverter);
log.info("--------------------扩展器start----------------");
/**
* 实现存储之前增强访问令牌的策略
*/
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> list = new ArrayList<>();
list.add(jwtTokenEnhancer);
list.add(jwtAccessTokenConverter);
tokenEnhancerChain.setTokenEnhancers(list);
/**
* 构造参数需要TokenEnhancer 它的实现类为 {@link TokenEnhancerChain}
*/
endpoints.tokenEnhancer(tokenEnhancerChain)
.accessTokenConverter(jwtAccessTokenConverter);
log.info("--------------------扩展器end----------------");
}
/**
* 授权服务器安全配置器
*
* <p>
* 放行那些端点配置, 例如我想要将生成的accessToken转换成用户信息,那么则需要将其开放,
* 默认为denyAll()
* <p>
* 具体参考 {@link AuthorizationServerSecurityConfigurer} 的 private String checkTokenAccess = "denyAll()";
* </P>
* <p>
* 此处访问的接口为: http: // 你的地址: 端口号: xxxx /oauth/check_token
* 具体参考 {@link org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration}
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("permitAll()");
}
}
然后重启项目取测试可以看到如下效果: (还是/oauth/token)