一、基于内存+token方式
1.认证中心配置
引入依赖
<!-- eurek依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
编辑yml配置文件
server:
port: 9999
spring:
application:
name: lagou-cloud-oauth-server
eureka:
client:
service-url:
defaultZone: http://LagouCloudEurekaServerA:8761/eureka/,http://LagouCloudEurekaServerB:8762/eureka/
registry-fetch-interval-seconds: 30 #多久拉取一次服务列表 默认30
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
主启动类
@EnableAuthorizationServer //开启认证服务器功能
@EnableDiscoveryClient
@SpringBootApplication
public class Oauth2ServerApplication9999 {
public static void main(String[] args) {
SpringApplication.run(Oauth2ServerApplication9999.class, args);
}
}
创建配置类(OauthServerConfig)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
/**
* Oauth2 Server配置类需要继承特定父类 AuthorizationServerConfigurerAdapter
*
* @remark
* @Author pengheng
* @date 2021/5/25 11:10
*/
@Configuration
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
/**
* 认证服务器通过api接口方式对外提供服务
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security);
security
//允许客户端表单认证
.allowFormAuthenticationForClients()
//开启/oauth/token_key的访问权限 默认=denyAll()
.tokenKeyAccess("permitAll()")
//开启端口/oauth/check_token的访问权限 默认=denyAll()
.checkTokenAccess("permitAll()");
}
/**
* 客户端详情配置
* 办法client_id等必要参数,表明客户端身份
*
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
super.configure(clients);
//客户端信息存储位置 可以在内存中,也可以在数据库中
clients.inMemory()
//添加客户端配置,指定client_id
.withClient("client_lagou")
//指定客户端所能访问的密码
.secret("abcxyz")
//指定客户端所能访问的资源
.resourceIds("autodeliver")
//认证类型/令牌颁发模式
.authorizedGrantTypes("password", "refresh_token")
//客户端权限范围
.scopes("all");
}
/**
* 配置token令牌相关信息(token存储在这里配置)
*
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
super.configure(endpoints);
endpoints
//指定token的存储方式
.tokenStore(tokenStore())
//token服务的描述,比如有效时间等
.tokenServices(authorizationServerTokenServices())
//指定认证管理器
.authenticationManager(authenticationManager)
//允许的http请求方式
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
/**
* 创建令牌存储对象(内存、数据库、jwt、redis)
*
* @return
*/
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
public AuthorizationServerTokenServices authorizationServerTokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
//是否支持令牌刷新
defaultTokenServices.setSupportRefreshToken(true);
//设置令牌生成方案
defaultTokenServices.setTokenStore(tokenStore());
//设置令牌有效时间(⼀般设置为2个⼩时)
defaultTokenServices.setAccessTokenValiditySeconds(20);
//令牌刷新时间
defaultTokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
return defaultTokenServices;
}
}
创建配置类(SecurityConfig)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.ArrayList;
/**
* @remark
* @Author pengheng
* @date 2021/5/25 13:39
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 注册⼀个认证管理器对象到容器
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 密码编码对象(密码不进⾏加密处理)
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 处理⽤户名和密码验证事宜
* 1)客户端传递username和password参数到认证服务器
* 2)⼀般来说,username和password会存储在数据库中的⽤户表中
* 3)根据⽤户表中数据,验证当前传递过来的⽤户信息的合法性
*/
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
// 在这个⽅法中就可以去关联数据库了,当前我们先把⽤户信息配置在内存中
// 实例化⼀个⽤户对象(相当于数据表中的⼀条⽤户记录)
UserDetails user = new User("admin", "123456", new ArrayList<>());
auth.inMemoryAuthentication().withUser(user).passwordEncoder(passwordEncoder);
}
}
校验服务器测试
- 生成token
http://10.0.0.45:9999/oauth/token?client_secret=abcxyz&grant_type=password&username=admin&password=123456&client_id=client_lagou
{
"access_token": "a7fb8e4d-304e-4dfb-b518-729a1a24f3f3",
"token_type": "bearer",
"refresh_token": "a3ad7612-29a0-49c8-81bd-ec1f0e30c50d",
"expires_in": 17,
"scope": "all"
}
- 校验token
http://localhost:9999/oauth/check_token?token=a7fb8e4d-304e-4dfb-b518-729a1a24f3f3
{
"aud": [
"autodeliver"
],
"active": true,
"exp": 1621927869,
"user_name": "admin",
"client_id": "client_lagou",
"scope": [
"all"
]
}
- 刷新token
http://localhost:9999/oauth/token?grant_type=refresh_token&client_id=client_lagou&client_secret=abcxyz&refresh_token=a3ad7612-29a0-49c8-81bd-ec1f0e30c50d
{
"access_token": "a7fb8e4d-304e-4dfb-b518-729a1a24f3f3",
"token_type": "bearer",
"refresh_token": "a3ad7612-29a0-49c8-81bd-ec1f0e30c50d",
"expires_in": 20,
"scope": "all"
}
2.资源服务器配置
引入依赖(与认证中心相同)
<!-- eureka 客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
创建配置类(ResourceServerConfig)
@Configuration
@EnableResourceServer //开启资源服务器功能
@EnableWebSecurity //开启web访问安全
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/**
* 该方法用于定义志愿服务期向远程服务器发起请求,进行token校验等事宜
*
* @param resources
* @throws Exception
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("autodeliver");
RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
//设置校验token的url
remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9999/oauth/check_token");
//设置客户端id和客户端密钥
remoteTokenServices.setClientId("client_lagou");
remoteTokenServices.setClientSecret("abcxyz");
resources.tokenServices(remoteTokenServices);
}
/**
* 配置API接口认证,设置指定接口过滤或放行
*
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeRequests()
//指定/autodeliver开头的都要验证
.antMatchers("/autodeliver/**").authenticated()
.antMatchers("/demo/**").authenticated()
.anyRequest().permitAll();
}
}
测试
请求被拦截
http://localhost:8096/autodeliver/checkState/1545132
响应:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
正常请求
http://localhost:8096/demo/test?access_token=2b9c1027-1525-40e3-8163-d6a8699b1a0a
响应:
demo-test
二、基于内存+Jwt 令牌方式
上述demo中,认证服务器颁发令牌都存在认证服务器内,后期访问资源服务器都要携带token去认证服务器去校验token有效性,如果资源服务器过多,认证服务器压力会很大。我们可以通过jwt机制进行改造,由于jwt是一个特殊的携带信息的加密字符,使用jwt机制之后资源服务器不需要访问认证服务器。JWT介绍
1.认证中心改造(修改OauthServerConfig配置)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.jwt.crypto.sign.MacSigner;
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.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
/**
* Oauth2 Server配置类需要继承特定父类 AuthorizationServerConfigurerAdapter
*
* @remark
* @Author pengheng
* @date 2021/5/25 11:10
*/
@Configuration
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
/**
* 认证服务器通过api接口方式对外提供服务
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security);
security
//允许客户端表单认证
.allowFormAuthenticationForClients()
//开启/oauth/token_key的访问权限 默认=denyAll()
.tokenKeyAccess("permitAll()")
//开启端口/oauth/check_token的访问权限 默认=denyAll()
.checkTokenAccess("permitAll()");
}
/**
* 客户端详情配置
* 办法client_id等必要参数,表明客户端身份
*
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
super.configure(clients);
//客户端信息存储位置 可以在内存中,也可以在数据库中
clients.inMemory()
//添加客户端配置,指定client_id
.withClient("client_lagou")
//指定客户端所能访问的密码
.secret("abcxyz")
//指定客户端所能访问的资源
.resourceIds("autodeliver")
//认证类型/令牌颁发模式
.authorizedGrantTypes("password", "refresh_token")
//客户端权限范围
.scopes("all");
}
/**
* 配置token令牌相关信息(token存储在这里配置)
*
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
super.configure(endpoints);
endpoints
//指定token的存储方式
.tokenStore(tokenStore())
//token服务的描述,比如有效时间等
.tokenServices(authorizationServerTokenServices())
//指定认证管理器
.authenticationManager(authenticationManager)
//允许的http请求方式
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
/**
* 创建令牌存储对象(内存、数据库、jwt、redis)
*
* @return
*/
public TokenStore tokenStore() {
// return new InMemoryTokenStore();
return new JwtTokenStore(jwtAccessTokenConverter());
}
private String sign_key = "pengheng";
/**
* 返回jwt令牌转换器
* @return
*/
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(sign_key);
jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));
return jwtAccessTokenConverter;
}
public AuthorizationServerTokenServices authorizationServerTokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
//是否支持令牌刷新
defaultTokenServices.setSupportRefreshToken(true);
//设置令牌生成方案
defaultTokenServices.setTokenStore(tokenStore());
//针对jwt令牌添加
defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
//设置令牌有效时间(⼀般设置为2个⼩时)
defaultTokenServices.setAccessTokenValiditySeconds(20);
//令牌刷新时间
defaultTokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
return defaultTokenServices;
}
}
整改后的令牌获取格式
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXV0b2RlbGl2ZXIiXSwiZXhwIjoxNjIxOTM2MzE5LCJ1c2VyX25hbWUiOiJhZG1pbiIsImp0aSI6ImZmNmFiOWJlLTg3YzAtNDI1ZC05MTUxLTIxZmQ1Njc5OGVhOCIsImNsaWVudF9pZCI6ImNsaWVudF9sYWdvdSIsInNjb3BlIjpbImFsbCJdfQ.nmYc1z1guIfoliUPKs9NxGd6GhS95F22yf5DcXgvvSI",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXV0b2RlbGl2ZXIiXSwidXNlcl9uYW1lIjoiYWRtaW4iLCJzY29wZSI6WyJhbGwiXSwiYXRpIjoiZmY2YWI5YmUtODdjMC00MjVkLTkxNTEtMjFmZDU2Nzk4ZWE4IiwiZXhwIjoxNjIyMTk1NDk5LCJqdGkiOiJhYmEyMWEwOC02ODNkLTRjM2QtYTY5MC1mZDMyMjVlOGY5ODciLCJjbGllbnRfaWQiOiJjbGllbnRfbGFnb3UifQ.bZdtkWEPrAghIDY4SKLDlgnQQho85qrNbPwg3OfrW2s",
"expires_in": 19,
"scope": "all",
"jti": "ff6ab9be-87c0-425d-9151-21fd56798ea8"
}
2.客户端改造(修改ResourceServerConfig配置)
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.jwt.crypto.sign.MacSigner;
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.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
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;
/**
* @remark
* @Author pengheng
* @date 2021/5/25 14:59
*/
@Configuration
@EnableResourceServer //开启资源服务器功能
@EnableWebSecurity //开启web访问安全
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/**
* 该方法用于定义志愿服务期向远程服务器发起请求,进行token校验等事宜
*
* @param resources
* @throws Exception
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// resources.resourceId("autodeliver");
// RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
// //设置校验token的url
// remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9999/oauth/check_token");
// //设置客户端id和客户端密钥
// remoteTokenServices.setClientId("client_lagou");
// remoteTokenServices.setClientSecret("abcxyz");
resources.resourceId("autodeliver").tokenStore(tokenStore()).stateless(true);//无状态设置
}
/**
* 配置API接口认证,设置指定接口过滤或放行
*
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeRequests()
//指定/autodeliver开头的都要验证
.antMatchers("/autodeliver/**").authenticated()
.antMatchers("/demo/**").authenticated()
.anyRequest().permitAll();
}
/**
* 创建令牌存储对象(内存、数据库、jwt、redis)
*
* @return
*/
public TokenStore tokenStore() {
// return new InMemoryTokenStore();
return new JwtTokenStore(jwtAccessTokenConverter());
}
private String sign_key = "pengheng";
/**
* 返回jwt令牌转换器
* @return
*/
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(sign_key);
jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));
return jwtAccessTokenConverter;
}
}
三、基于数据库加载用户信息+Jwt方式
修改认证中心
- 创建数据库
DROP DATABASE IF EXISTS `oauth2`;
CREATE DATABASE oauth2;
USE oauth2;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` VARCHAR ( 48 ) NOT NULL,
`resource_ids` VARCHAR ( 256 ) DEFAULT NULL,
`client_secret` VARCHAR ( 256 ) DEFAULT NULL,
`scope` VARCHAR ( 256 ) DEFAULT NULL,
`authorized_grant_types` VARCHAR ( 256 ) DEFAULT NULL,
`web_server_redirect_uri` VARCHAR ( 256 ) DEFAULT NULL,
`authorities` VARCHAR ( 256 ) DEFAULT NULL,
`access_token_validity` INT ( 11 ) DEFAULT NULL,
`refresh_token_validity` INT ( 11 ) DEFAULT NULL,
`additional_information` VARCHAR ( 4096 ) DEFAULT NULL,
`autoapprove` VARCHAR ( 256 ) DEFAULT NULL,
PRIMARY KEY ( `client_id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8;-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
BEGIN;
INSERT INTO `oauth_client_details` VALUES ( 'client_lagou123', 'autodeliver,resume', 'abcxyz', 'all', 'password,refresh_token', NULL, NULL, 7200, 259200, NULL, NULL );
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` char(10) DEFAULT NULL,
`password` char(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of users
-- ----------------------------
BEGIN;
INSERT INTO `users` VALUES (4, 'lagou-user', 'iuxyzds');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
- 引入数据库依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
- 修改application.yml配置引入数据源
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/oauth2??useUnicode=true&serverTimezone=UTC&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
username: root
password: root
druid:
initial-size: 10
min-idle: 10
max-active: 30
max-wait: 50000
- 认证中心配置类改造
// 注入数据源
@Autowired DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
super.configure(clients);
//客户端信息存储位置 可以在内存中,也可以在数据库中
// clients.inMemory()
// //添加客户端配置,指定client_id
// .withClient("client_lagou")
// //指定客户端所能访问的密码
// .secret("abcxyz")
// //指定客户端所能访问的资源
// .resourceIds("autodeliver")
// //认证类型/令牌颁发模式
// .authorizedGrantTypes("password", "refresh_token")
// //客户端权限范围
// .scopes("all");
clients.withClientDetails(jdbcClientDetailsService());
}
@Bean
public JdbcClientDetailsService jdbcClientDetailsService(){
return new JdbcClientDetailsService(dataSource);
}
- 开发UserDetailsService接⼝的实现类,根据⽤户名从数据库加载⽤户信息
@Service
public class JdbcUserDetailService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users users = userRepository.findByUsername(username);
return new User(users.getUsername(), users.getPassword(), new ArrayList<>());
}
}
-
修改SecurityConfig配置
import com.lagou.edu.service.JdbcUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JdbcUserDetailService jdbcUserDetailService;
/**
* 注册⼀个认证管理器对象到容器
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 密码编码对象(密码不进⾏加密处理)
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 处理⽤户名和密码验证事宜
* 1)客户端传递username和password参数到认证服务器
* 2)⼀般来说,username和password会存储在数据库中的⽤户表中
* 3)根据⽤户表中数据,验证当前传递过来的⽤户信息的合法性
*/
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
// 在这个⽅法中就可以去关联数据库了,当前我们先把⽤户信息配置在内存中
// 实例化⼀个⽤户对象(相当于数据表中的⼀条⽤户记录)
// UserDetails user = new User("admin", "123456", new ArrayList<>());
// auth.inMemoryAuthentication().withUser(user).passwordEncoder(passwordEncoder);
//数据库校验用户信息
auth.userDetailsService(jdbcUserDetailService);
}
}
四、token添加自定义信息
1.认证中心配置
重写AccessTokenConverter
@Component
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {
@Override
public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
Map<String, Object> stringMap = (Map<String, Object>) super.convertAccessToken(token, authentication);
stringMap.put("hello", ThreadLocalRandom.current().nextInt(10));
return stringMap;
}
}
OauthServerConfig配置添加自定义转换器
@Autowired
private CustomAccessTokenConverter customAccessTokenConverter;
/**
* 返回jwt令牌转换器
* @return
*/
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(sign_key);
jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));
//设置请求token转换器
jwtAccessTokenConverter.setAccessTokenConverter(customAccessTokenConverter);
return jwtAccessTokenConverter;
}
2.资源服务器配置
重写CustomAccessTokenConverter
@Component
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
OAuth2Authentication oAuth2Authentication = super.extractAuthentication(map);
oAuth2Authentication.setDetails(map);
return oAuth2Authentication;
}
}
获取Token存放内容方法
Object details = SecurityContextHolder.getContext().getAuthentication().getDetails();