mirror of
https://gitee.com/many2many/java-web.git
synced 2025-01-11 14:40:55 +08:00
Compare commits
2 Commits
bb6867d6d6
...
a1c65ac65f
Author | SHA1 | Date | |
---|---|---|---|
a1c65ac65f | |||
f99948603b |
@ -119,7 +119,7 @@ nginx中部署 [paopao-web-ui.zip](../examples/chapter07/paopao-web-ui.zip)
|
|||||||
|
|
||||||
### 7.2 用户注册功能开发
|
### 7.2 用户注册功能开发
|
||||||
|
|
||||||
基本上按照层次结构创建系统的包结构, 在`com.lk.paopao`包下创建子包`controllers`、`services`、`entities`、`repositories`。
|
基本上按照层次结构创建系统的包结构, 在`com.lk.paopao`包下创建子包`controller`、`service`、`entity`、`repository`、`config`。
|
||||||
|
|
||||||
|
|
||||||
#### 7.2.1 API设计
|
#### 7.2.1 API设计
|
||||||
@ -158,19 +158,89 @@ spring:
|
|||||||
```
|
```
|
||||||
yml配置文件的格式需要注意:缩进和空格是敏感的,需要严格遵循格式。
|
yml配置文件的格式需要注意:缩进和空格是敏感的,需要严格遵循格式。
|
||||||
|
|
||||||
#### 7.2.3 实体类和Repository接口
|
#### 7.2.3 实体类User和UserRepository接口
|
||||||
|
|
||||||
设计和实现实体类User
|
创建实体类User
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
对应的数据库接口: UserRepository
|
创建User对应的数据库接口: UserRepository
|
||||||
|
|
||||||
|
|
||||||
|
#### 7.2.3 创建服务类AuthService
|
||||||
|
|
||||||
#### 7.2.3 Controller和service
|
|
||||||
|
|
||||||
#### 7.2.4 解决跨域问题
|
创建service类,处理用户注册和登录逻辑: AuthService
|
||||||
|
|
||||||
|
#### 7.2.4 创建AuthController类
|
||||||
|
|
||||||
|
创建controller类,处理用户注册和登录请求: AuthController
|
||||||
|
|
||||||
|
#### 7.2.5 解决跨域问题
|
||||||
|
|
||||||
|
跨域问题是指在使用AJAX进行跨域请求时,浏览器阻止访问不同源的Web资源的问题。跨域问题的本质是浏览器的一种安全机制,称为同源策略(Same-Origin Policy)。同源策略限制了来自不同源的文档或脚本间的交互,以防止恶意网站窃取数据或进行其他恶意操作。
|
||||||
|
|
||||||
|
跨域问题的原因
|
||||||
|
|
||||||
|
跨域问题主要由以下几种情况引起:
|
||||||
|
|
||||||
|
协议不同:例如,http和https。
|
||||||
|
域名不同:例如,example.com和test.com。
|
||||||
|
端口不同:例如,80和8080。
|
||||||
|
|
||||||
|
目前最常用的解决方案:
|
||||||
|
|
||||||
|
> CORS(Cross-Origin Resource Sharing):服务器端设置CORS头部信息,允许特定的域名或所有域名进行跨域请求。
|
||||||
|
|
||||||
|
在spring boot中可使用自己创建CorsFilter的bean实例的方式解决。
|
||||||
|
|
||||||
|
在项目`config`包中创建类`CorsConfig`,添加如下代码:
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.lk.paopao.config;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置类,用于设置跨域请求
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CorsConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并配置CorsFilter bean
|
||||||
|
*
|
||||||
|
* @return CorsFilter bean实例,用于处理跨域请求
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public CorsFilter corsFilter() {
|
||||||
|
// 创建URL基于的Cors配置源
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
// 创建Cors配置实例
|
||||||
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
|
|
||||||
|
// 配置允许的源
|
||||||
|
config.setAllowedOrigins(List.of("http://localhost"));
|
||||||
|
// 配置允许的方法
|
||||||
|
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
|
||||||
|
// 配置允许的头部
|
||||||
|
config.setAllowedHeaders(List.of("*"));
|
||||||
|
// 允许凭据
|
||||||
|
config.setAllowCredentials(true);
|
||||||
|
|
||||||
|
// 在配置源中注册全局跨域配置
|
||||||
|
source.registerCorsConfiguration("/**", config);
|
||||||
|
// 返回CorsFilter实例
|
||||||
|
return new CorsFilter(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
### 7.3 发布文章功能开发
|
### 7.3 发布文章功能开发
|
||||||
|
|
||||||
|
@ -25,6 +25,11 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
runtimeOnly 'com.h2database:h2:2.1.214'
|
runtimeOnly 'com.h2database:h2:2.1.214'
|
||||||
|
|
||||||
|
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
|
||||||
|
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
|
||||||
|
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5'
|
||||||
|
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,9 @@ public class CorsConfig {
|
|||||||
CorsConfiguration config = new CorsConfiguration();
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
|
|
||||||
// 配置允许的源
|
// 配置允许的源
|
||||||
config.setAllowedOrigins(List.of("http://localhost"));
|
config.setAllowedOrigins(List.of("http://localhost","http://localhost:8880"));
|
||||||
// 配置允许的方法
|
// 配置允许的方法
|
||||||
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
|
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
||||||
// 配置允许的头部
|
// 配置允许的头部
|
||||||
config.setAllowedHeaders(List.of("*"));
|
config.setAllowedHeaders(List.of("*"));
|
||||||
// 允许凭据
|
// 允许凭据
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.lk.paopao.jwt;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置类,用于在Spring Boot应用中注册JWT过滤器
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class JwtConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动注入JwtFilter,用于处理JWT相关的请求
|
||||||
|
*/
|
||||||
|
@Autowired
|
||||||
|
private JwtFilter jwtFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并配置JwtFilter的FilterRegistrationBean
|
||||||
|
*
|
||||||
|
* @return FilterRegistrationBean<JwtFilter> 配置了JwtFilter的FilterRegistrationBean实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean<JwtFilter> loggingFilter() {
|
||||||
|
// 创建FilterRegistrationBean对象,用于注册JwtFilter
|
||||||
|
FilterRegistrationBean<JwtFilter> registrationBean = new FilterRegistrationBean<>();
|
||||||
|
// 设置过滤器实例
|
||||||
|
registrationBean.setFilter(jwtFilter);
|
||||||
|
// 添加需要过滤的URL模式
|
||||||
|
registrationBean.addUrlPatterns("/v1/user/*","/v1/post/*");
|
||||||
|
// 返回配置好的FilterRegistrationBean
|
||||||
|
return registrationBean;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package com.lk.paopao.jwt;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT过滤器,用于处理每次请求,验证JWT令牌并设置用户认证信息。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class JwtFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtService jwtService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心过滤方法,处理每次请求。
|
||||||
|
*
|
||||||
|
* @param request 请求对象,用于获取请求头中的JWT令牌。
|
||||||
|
* @param response 响应对象,用于处理响应。
|
||||||
|
* @param filterChain 过滤链,用于继续处理请求链。
|
||||||
|
* @throws ServletException 如果过滤过程中发生Servlet异常。
|
||||||
|
* @throws IOException 如果过滤过程中发生IO异常。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
try {
|
||||||
|
// 解析JWT令牌。
|
||||||
|
String jwt = parseJwt(request);
|
||||||
|
if (jwt != null && jwtService.validateToken(jwt)) {
|
||||||
|
// 从JWT令牌中获取用户名。
|
||||||
|
String username = jwtService.getUserNameFromJwtToken(jwt);
|
||||||
|
|
||||||
|
// 将用户名设置为请求属性,以便后续处理使用。
|
||||||
|
request.setAttribute("username", username);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 记录无法设置用户认证的错误。
|
||||||
|
logger.error("Cannot set user authentication:", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 继续处理请求链。
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析请求中的JWT令牌。
|
||||||
|
*
|
||||||
|
* @param request 请求对象,用于获取请求头中的JWT令牌。
|
||||||
|
* @return 如果解析成功,则返回JWT令牌字符串;否则返回null。
|
||||||
|
*/
|
||||||
|
private String parseJwt(HttpServletRequest request) {
|
||||||
|
// 从请求头中获取"Authorization"字段。
|
||||||
|
String headerAuth = request.getHeader("Authorization");
|
||||||
|
// 如果存在且以"Bearer "开头,则提取并返回令牌字符串。
|
||||||
|
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
|
||||||
|
return headerAuth.substring(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不符合条件,则返回null。
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
package com.lk.paopao.jwt;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.io.Decoders;
|
||||||
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT服务类,用于生成和验证JWT Token
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class JwtService {
|
||||||
|
|
||||||
|
// JWT密钥,用于签名和验证
|
||||||
|
private static final String SECRET = "uYuVw0mke38MfLhO19wUQyRgwrmYo89ibpQTXPHi4vg=";
|
||||||
|
|
||||||
|
// JWT过期时间,单位:毫秒
|
||||||
|
private static final long JWT_EXPIRATION_MS = 1000 * 3600 * 24;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成JWT Token
|
||||||
|
*
|
||||||
|
* @param username 用户名,作为JWT的主题
|
||||||
|
* @return 生成的JWT Token字符串
|
||||||
|
*/
|
||||||
|
public String generateJwtToken(String username) {
|
||||||
|
// 创建JWT,设置主题、签发时间、过期时间和签名密钥
|
||||||
|
return Jwts.builder().subject(username)
|
||||||
|
.issuedAt(new Date())
|
||||||
|
.expiration(new Date(new Date().getTime() + JWT_EXPIRATION_MS))
|
||||||
|
.signWith(getSigningKey())
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取签名密钥
|
||||||
|
*
|
||||||
|
* @return 签名所用的SecretKey
|
||||||
|
*/
|
||||||
|
private SecretKey getSigningKey() {
|
||||||
|
// 解码JWT密钥
|
||||||
|
byte[] keyBytes = Decoders.BASE64.decode(SECRET);
|
||||||
|
return Keys.hmacShaKeyFor(keyBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从JWT Token中提取用户名
|
||||||
|
*
|
||||||
|
* @param token JWT Token字符串
|
||||||
|
* @return 提取的用户名
|
||||||
|
*/
|
||||||
|
public String getUserNameFromJwtToken(String token) {
|
||||||
|
// 解析JWT Token并提取主题(用户名)
|
||||||
|
return Jwts.parser()
|
||||||
|
.verifyWith(getSigningKey())
|
||||||
|
.build()
|
||||||
|
.parseSignedClaims(token)
|
||||||
|
.getPayload()
|
||||||
|
.getSubject();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证JWT Token的有效性
|
||||||
|
*
|
||||||
|
* @param authToken JWT Token字符串
|
||||||
|
* @return 如果Token有效返回true,否则返回false
|
||||||
|
*/
|
||||||
|
public boolean validateToken(String authToken) {
|
||||||
|
try {
|
||||||
|
// 尝试解析JWT Token,如无异常则表示Token有效
|
||||||
|
Jwts.parser()
|
||||||
|
.verifyWith(getSigningKey())
|
||||||
|
.build()
|
||||||
|
.parseSignedClaims(authToken);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 记录JWT签名异常
|
||||||
|
System.out.println("Invalid JWT signature: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
examples/chapter07/paopao/src/main/resources/application.yml
Normal file
23
examples/chapter07/paopao/src/main/resources/application.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
spring:
|
||||||
|
jackson:
|
||||||
|
property-naming-strategy: SNAKE_CASE # 驼峰转下划线
|
||||||
|
datasource:
|
||||||
|
url: jdbc:h2:file:./db.h2 # 使用文件存储
|
||||||
|
driverClassName: org.h2.Driver
|
||||||
|
username: root
|
||||||
|
password: root
|
||||||
|
h2:
|
||||||
|
console: # 开启console访问 默认false
|
||||||
|
enabled: true
|
||||||
|
settings:
|
||||||
|
trace: true # 开启h2 console 跟踪 方便调试 默认 false
|
||||||
|
web-allow-others: true # 允许console 远程访问 默认false
|
||||||
|
path: /h2 # h2 访问路径上下文
|
||||||
|
jpa:
|
||||||
|
show-sql: true
|
||||||
|
open-in-view: false
|
||||||
|
defer-datasource-initialization: true
|
||||||
|
database-platform: org.hibernate.dialect.H2Dialect
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: update
|
Loading…
Reference in New Issue
Block a user