如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
源码地址:https://gitee.com/csps/mingyue
文档地址:https://gitee.com/csps/mingyue/wikis
Idea 创建 maven 项目这儿就不多赘述了,相信各位大佬都是信手拈来~
父项目的依赖都放在这儿了,后续用到什么再增加什么
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.csp.mingyuegroupId>
<artifactId>mingyueartifactId>
<version>1.0.0version>
<packaging>pompackaging>
<name>MingYuename>
<description>MingYue 微服务系统description>
<properties>
<mingyue.version>1.0.0mingyue.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<java.version>1.8java.version>
<spring-boot.version>2.7.11spring-boot.version>
<spring-cloud.version>2021.0.6spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.5.0spring-cloud-alibaba.version>
<jasypt.version>3.0.5jasypt.version>
<hutool.version>5.8.18hutool.version>
<lombok.version>1.18.26lombok.version>
<spring.checkstyle.plugin>0.0.38spring.checkstyle.plugin>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>com.github.ulisesbocchiogroupId>
<artifactId>jasypt-spring-boot-starterartifactId>
<version>${jasypt.version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${spring-boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${spring-cloud-alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-bomartifactId>
<version>${hutool.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
project>
Idea
mingyue
右击 ->New
->Module
<properties>
<sa-token.version>1.34.0sa-token.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>cn.dev33groupId>
<artifactId>sa-token-spring-boot-starterartifactId>
<version>${sa-token.version}version>
dependency>
<dependency>
<groupId>cn.dev33groupId>
<artifactId>sa-token-oauth2artifactId>
<version>${sa-token.version}version>
dependency>
<dependency>
<groupId>cn.dev33groupId>
<artifactId>sa-token-dao-redis-jacksonartifactId>
<version>${sa-token.version}version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
dependencies>
/**
* Sa-OAuth2 Server端 控制器
* @author kong
*
*/
@RestController
public class SaOAuth2ServerController {
// 处理所有OAuth相关请求
@RequestMapping("/oauth2/*")
public Object request() {
System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
return SaOAuth2Handle.serverRequest();
}
// Sa-OAuth2 定制化配置
@Autowired
public void setSaOAuth2Config(SaOAuth2Config cfg) {
cfg.
// 未登录的视图
setNotLoginView(()->{
return new ModelAndView("login.html");
}).
// 登录处理函数
setDoLoginHandle((name, pwd) -> {
if("sa".equals(name) && "123456".equals(pwd)) {
StpUtil.login(10001);
return SaResult.ok();
}
return SaResult.error("账号名或密码错误");
}).
// 授权确认视图
setConfirmView((clientId, scope)->{
Map<String, Object> map = new HashMap<>();
map.put("clientId", clientId);
map.put("scope", scope);
return new ModelAndView("confirm.html", map);
})
;
}
// 全局异常拦截
@ExceptionHandler
public SaResult handlerException(Exception e) {
e.printStackTrace();
return SaResult.error(e.getMessage());
}
// ---------- 开放相关资源接口: Client端根据 Access-Token ,置换相关资源 ------------
// 获取Userinfo信息:昵称、头像、性别等等
@RequestMapping("/oauth2/userinfo")
public SaResult userinfo() {
// 获取 Access-Token 对应的账号id
String accessToken = SaHolder.getRequest().getParamNotNull("access_token");
Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
System.out.println("-------- 此Access-Token对应的账号id: " + loginId);
// 校验 Access-Token 是否具有权限: userinfo
SaOAuth2Util.checkScope(accessToken, "userinfo");
// 模拟账号信息 (真实环境需要查询数据库获取信息)
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("nickname", "shengzhang_");
map.put("avatar", "http://xxx.com/1.jpg");
map.put("age", "18");
map.put("sex", "男");
map.put("address", "山东省 青岛市 城阳区");
return SaResult.data(map);
}
}
/**
* Sa-Token OAuth2.0 整合实现
* @author kong
*/
@Component
public class SaOAuth2TemplateImpl extends SaOAuth2Template {
// 根据 id 获取 Client 信息
@Override
public SaClientModel getClientModel(String clientId) {
// 此为模拟数据,真实环境需要从数据库查询
if("1001".equals(clientId)) {
return new SaClientModel()
.setClientId("1001")
.setClientSecret("aaaa-bbbb-cccc-dddd-eeee")
.setAllowUrl("*")
.setContractScope("userinfo")
.setIsAutoMode(true);
}
return null;
}
// 根据ClientId 和 LoginId 获取openid
@Override
public String getOpenid(String clientId, Object loginId) {
// 此为模拟数据,真实环境需要从数据库查询
return "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__";
}
}
@SpringBootApplication
public class MingYueAuthApplication {
public static void main(String[] args) {
SpringApplication.run(MingYueAuthApplication.class, args);
System.out.println("\nSa-Token-OAuth Server端启动成功: http://localhost:9000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=https://sa-token.cc&scope=userinfo");
}
}
启动项目,访问打印地址:
Sa-Token-OAuth Server端启动成功: http://localhost:9000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=https://sa-token.cc&scope=userinfo
共四种模式,分别是授权码(Authorization Code)、隐藏式(Implicit)、密码式(Password)、凭证式(Client Credentials),本文介绍了两种常用的:授权码(Authorization Code)、密码式(Password)
http://localhost:9000/oauth2/authorize
?response_type=code
&client_id=1001
&redirect_uri=https://sa-token.cc&scope=userinfo
参数详解:
参数 | 是否必填 | 说明 |
---|---|---|
response_type | 是 | 返回类型,这里请填写:code |
client_id | 是 | 应用id |
redirect_uri | 是 | 用户确认授权后,重定向的url地址 |
scope | 否 | 具体请求的权限,多个用逗号隔开 |
state | 否 | 随机值,此参数会在重定向时追加到url末尾,不填不追加 |
Code授权码具有以下特点:
- 每次授权产生的 Code 码都不一样
- Code码用完即废,不能二次使用
- 一个Code的有效期默认为五分钟,超时自动作废
- 每次授权产生新 Code 码,会导致旧 Code 码立即作废,即使旧 Code 码尚未使用
http://localhost:9000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=https://sa-token.cc&scope=userinfo
打开地址,登录并同意授权
地址栏拿到
code
https://sa-token.cc/?code=ct0RB10UkGSwXINdBkGRGVKBvoKjIi6jzeYi3NFrCwoXzDr36Ljgm1ZBplYx
浏览器直接访问:
http://localhost:9000/oauth2/token
?grant_type=authorization_code
&client_id=1001
&client_secret=aaaa-bbbb-cccc-dddd-eeee
&code=ktN6FvIfB7pKqp7Thvkm32EfUhbveoybwOtmvqCCbuLdxevmyr9FW09Kd6qL
参数 | 是否必填 | 说明 |
---|---|---|
grant_type | 是 | 授权类型,这里请填写:authorization_code |
client_id | 是 | 应用id |
client_secret | 是 | 应用秘钥 |
code | 是 | 步骤1.1中获取到的授权码 |
打印如下:
{
"code": 200,
"msg": "ok",
"data": {
"access_token": "E1ZLgNXLiUIz8DmAxmgPeegMPsUCEJJLjUV9uwDQWCu4f6Tgg8U5JqKSrqSx",
"refresh_token": "0IAWolcbMvoIpF8F1JcEuytfcoTe8nNVwsEdUHOy7rBX0V99vhPBvtFGfh62",
"expires_in": 7199,
"refresh_expires_in": 2591999,
"client_id": "1001",
"scope": "userinfo",
"openid": "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"
}
}
首先在 Client 端构建表单,让用户输入 Server 端的账号和密码,然后在 Client 端访问接口
http://localhost:9000/oauth2/token
?grant_type=password
&client_id=1001
&client_secret=aaaa-bbbb-cccc-dddd-eeee
&username=sa
&password=123456
参数 | 是否必填 | 说明 |
---|---|---|
grant_type | 是 | 返回类型,这里请填写:password |
client_id | 是 | 应用id |
client_secret | 是 | 应用秘钥 |
username | 是 | 用户的Server端账号 |
password | 是 | 用户的Server端密码 |
scope | 否 | 具体请求的权限,多个用逗号隔开 |
浏览器直接访问,打印如下:
{
"code": 200,
"msg": "ok",
"data": {
"access_token": "BvU1yja6sy4WT3orCvIiYExIOhGMBwVps38ZywZWGaeYaM6nJbPkJRbHpmSC",
"refresh_token": "TzSGmGhtPuAZUZ5ndfVETdmmpGr3ZwAH5N5kbrEYeeR2LKbLBXEysqsLxuV6",
"expires_in": 7199,
"refresh_expires_in": 2591999,
"client_id": "1001",
"scope": "",
"openid": "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"
}
}
至此,一个基于 OAuth2 的认证中心 Demo 就完成啦,接下来我们先优化一下代码结构。