权限管理系统
项目概述
业务架构
技术架构
SpringBoot Starter 原理
SpringBoot 简化 Spring 的原因就是提供了许多 starter,而 starter 本质上就是引入了一些相关依赖和进行一些初始化的自动配置。
打包依赖
自动配置
自动配置原理
基于 Java 代码的 Bean 配置
@Configuration + @Bean
自动配置条件依赖
@Conditional
Bean 参数获取
@EnableConfigurationProperties + @ConfigurationProperties(读取配置文件,转换为 Bean 对象)
Bean 的发现
@EnableAutoConfiguration + @Import
(解决的是 jar 包中的自动配置类
XXXAutoConfiguration
这些是如何被我们的@SpringBootApplication
发现的)Bean 的加载
项目搭建
Nacos 配置
修改 Nacos 的配置文件,将由 Nacos 管理的数据保存到自定义的数据库中,而不是保存到 Nacos 内部的数据库。
修改
conf/application.properties
文件spring.datasource.platform=mysql ## Count of DB: db.num=1 ## Connect URL of DB: db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user.0=root db.password.0=root
运行
conf
目录下的 sql 文件一般一个项目对应一个 namespace,默认情况下是 public,这里创建一个自定义的命名空间,同时可以在表中查看到相应的变化
在 Nacos 中导入一些和项目相关的配置文件
JWT
不使用签名
@Test
public void jwtNoSignTest() {
Map<String, Object> header = new HashMap<>();
header.put("alg", "none");
header.put("type", "JWT");
Map<String, Object> body = new HashMap<>();
body.put("userId", "100");
body.put("role", "admin");
// 生成JWT Token
String jwtToken = Jwts.builder()
.setHeader(header)
.setClaims(body)
.compact();
System.out.println(jwtToken);
Jwt jwt = Jwts.parser()
.parse(jwtToken);
// Header 和 Body 直接打印输出就是 hash 表
Header jwtHeader = jwt.getHeader();
System.out.println("jwtHeader = " + jwtHeader);
Object alg = jwtHeader.get("alg");
System.out.println(alg);
Map<String, Object> jwtBody = (Map<String, Object>) jwt.getBody();
System.out.println("jwtBody = " + jwtBody);
Object userId = jwtBody.get("userId");
System.out.println(userId);
}
使用 HS256 对称加密算法签名
@Test
public void jwtWithSignTest() {
String secretKey = "HelloWorld";
Map<String, Object> header = new HashMap<>();
header.put("alg", SignatureAlgorithm.HS256.getValue());
header.put("type", "JWT");
Map<String, Object> body = new HashMap<>();
body.put("userId", "100");
body.put("role", "admin");
String jwtToken = Jwts.builder()
.setHeader(header)
.setClaims(body)
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
System.out.println("jwtToken = " + jwtToken);
Jwt jwt = Jwts.parser()
.setSigningKey(secretKey)
.parse(jwtToken);
Header jwtHeader = jwt.getHeader();
Object jwtBody = jwt.getBody();
System.out.println("jwtHeader = " + jwtHeader);
System.out.println("jwtBody = " + jwtBody);
}
使用 RS256 非对称加密算法签名
private byte[] readBytesFromFile(String filePath) {
byte[] result;
InputStream inputStream = null;
DataInputStream dataInputStream = null;
try {
inputStream = ClassLoader.getSystemResourceAsStream(filePath);
dataInputStream = new DataInputStream(inputStream);
result = new byte[dataInputStream.available()];
dataInputStream.readFully(result);
return result;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (dataInputStream != null) {
dataInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Test
public void jwtRS256Test() throws Exception {
//TODO: 这里需要替换成从指定的密钥文件读取的字节
byte[] privateKeyBytes = readBytesFromFile("pri.key");
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(privateKeySpec);
byte[] publicKeyBytes = readBytesFromFile("pub.key");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(publicKeySpec);
Map<String, Object> header = new HashMap<>();
Map<String, Object> body = new HashMap<>();
String jwtToken = Jwts.builder()
.setHeader(header)
.setClaims(body)
.signWith(SignatureAlgorithm.RS256, privateKey)
.compact();
Jwt jwt = Jwts.parser()
.setSigningKey(publicKey)
.parse(jwtToken);
Header jwtHeader = jwt.getHeader();
Object jwtBody = jwt.getBody();
System.out.println("jwtHeader = " + jwtHeader);
System.out.println("jwtBody = " + jwtBody);
}
参数解析器
引入 @EnableLoginArgsResolver
注解才开启相应的功能,这种功能的实现是通过在配置类中完成相应功能的设置,但是此时配置类不添加 @Configuration
,即该类拥有配置类的功能,但是不被 Spring 框架的组件扫描发现(不被认为是配置类)。而 @EnableLoginArgsResolver
做的事情可以认为是就是 @Import(XXXConfig)
,即对 @Import
注解的封装,从而让 Spring 框架发现该配置类。
异常处理
如果不进行异常处理,那么程序中如果发生异常,则异常错误会直接显示在页面上,造成不友好的用户体验