SpringCloud Oauth2.0 实现资源验证
前提
* JDK1.8
* SpringCloud Greenwich.RELEASE
* SpringBoot 2.1.8.RELEASE
注
先看一下项目结构
现在开始搭建,首先建立一个maven项目,即父maven工程,pom文件内容如下
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.1.8.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mysql.version>8.0.16</mysql.version>
</properties>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<!-- 控制spring-cloud版本 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<!--druid依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<!--代码生成-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<!--swagger 文档注释-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<!--swagger-->
<!--fast-json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--log4j druid使用-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--健康检查-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
此处引入了基本都需要使用的依赖,并使用dependencyManagement 固定使用依赖的版本
下面开始搭建授权服务
新建springBoot工程,pom如下,指定父pom为上面的文件,并引入auth需要的jar包,
<parent>
<groupId>com.tz</groupId>
<artifactId>spring-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 断路器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
</dependency>
<!-- ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 重试机制 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
配置AuthorizationServer
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
/**
* 令牌存储
*/
@Autowired
private TokenStore tokenStore;
/**
* 客户端管理
*/
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthenticationManager authenticationManager;
/**
* A 配置客户端详情,支持哪些客户端
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
// 客户端id
.withClient("c1")
// 客户端密钥
.secret(new BCryptPasswordEncoder().encode("secret"))
// 资源列表
.resourceIds("res1")
// 该client允许的授权类型,
.authorizedGrantTypes("authorization_code","password","client_credentials",
"implicit","refresh_token")
// 允许的授权范围,就是一个标识,read,write
.scopes("all")
.autoApprove(false)
// 验证回调地址
.redirectUris("http://www.baidu.com");
}
/**
* B 令牌访问端点,即url
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
// 密码模式需要
.authenticationManager(authenticationManager)
// 授权码模式需要
.authorizationCodeServices(authorizationCodeServices)
// 令牌管理服务
.tokenServices(tokenServices())
//允许post提交
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
/**
* C 配置令牌端点 安全约束
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// /oauth/token_key url 公开
.tokenKeyAccess("permitAll()")
// /oauth/check_token 公开
.checkTokenAccess("permitAll()")
// 表单验证,申请令牌
.allowFormAuthenticationForClients();
}
/**
* D 定义tokenServices
* @return
*/
@Bean
public AuthorizationServerTokenServices tokenServices(){
DefaultTokenServices services = new DefaultTokenServices();
services.setClientDetailsService(clientDetailsService);
services.setSupportRefreshToken(true);
services.setTokenStore(tokenStore);
// 令牌默认有限期2小时
services.setAccessTokenValiditySeconds(7200);
// 刷新令牌默认有限期3天
services.setRefreshTokenValiditySeconds(259200);
return services;
}
/**
* 设置授权码如何存储
* @param
* @return
*/
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
}
}
定义令牌存储方式,TokenConfig
@Configuration
public class TokenConfig {
/**
* 令牌存储测试
*/
@Bean
public TokenStore tokenStore(){
// 内存方式,普通令牌
return new InMemoryTokenStore();
}
}
定义web配置,配置拦截url
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 认证管理器
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManager() throws Exception{
return super.authenticationManager();
}
/**
* 密码编码器
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* D 安全拦截机制
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 关闭csrf
http.csrf().disable()
// 开启验证
.authorizeRequests()
// 访问/r/r1需要p1
.antMatchers("/r/r1").hasAnyAuthority("p1")
// login* 不需要拦截
.antMatchers("/login*").permitAll()
// 其他的url都需要拦截
.anyRequest().authenticated()
.and()
// 支持表单登录
.formLogin();
}
}
application.properties
因为引入了数据源,所以此处需要配置,但是此时没有用到,后续用到时使用
spring.application.name=oauth-server
server.port=8762
spring.main.allow-bean-definition-overriding=true
logging.level.root = info
loggin.level.org.springframework.web = info
server.servlet.context-path=/oauth
# 数据源配置 start
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud-oauth2?useUnicode=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#durid登陆账号密码
loginUsername=root
loginPassword=root
# 下面为连接池的补充设置,应用到上面所有数据源中
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙,防止sql注入
spring.datasource.filters=stat,wall,log4j
spring.datasource.logSlowSql=true
# 数据源配置end
eureka.client.enabled=false
eureka.client.allow-redirects=false
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka/
eureka.instance.preferIpAddress = true
eureka.instance.instance-id = ${spring.application.name}:${spring.cloud.client.ip-address}:${spring.application.instance_id:${server.port}}
management.endpoints.web.exposure.include=refresh,health,info,env
feign.hystrix.enabled=true
feign.compression.request.enabled=true
feign.compression.request.mime-types[0]=text/html
feign.compression.request.mime-types[1]=application/xml
feign.compression.request.mime-types[2]=application/json
feign.compression.request.min-request-size=2048
feign.compression.response.enabled=true
#mybatis-plus配置控制台打印完整带参数SQL语句
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
security.oauth2.client.access-token-uri=http://localhost:8762/auth/oauth/token
security.oauth2.client.user-authorization-uri=http://localhost:8762/auth/oauth/authorize
security.oauth2.client.authorized-grant-types=authorization_code
security.oauth2.resource.user-info-uri=http://localhost:8780/dashboard/user
客户端模式,post 请求获取token
http://127.0.0.1:8762/oauth/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials
返回结果如下
用户名密码模式
定义userDetailService
@Service
@Slf4j
public class MyUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 用户名tz,密码123
UserDetails userDetails = User.withUsername("tz").password("$2a$10$pTffBwh9mawjeGG9K5ZhbenBfWQRV1aFVgZqVt59eM67iJhDqARyG").authorities("p1").build();
return userDetails;
}
加密的密码生成,生成的密码每次都不一样,但是都代表123
@Test
public void createBCryptPw(){
String gensalt = BCrypt.gensalt();
System.out.println(gensalt);
String hashpw = BCrypt.hashpw("123", gensalt);
System.out.println(hashpw);
}
用户名密码 post 请求,会返回refresh_token
http://127.0.0.1:8762/oauth/oauth/token?client_id=c1&client_secret=secret&username=tz&password=123&grant_type=password
返回结果
好了,授权服务器简单模式搭建完成,详细点击原文链接查看README,欢迎继续关注下一篇客户资源服务器搭建
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?