nginx
nginx反向代理
將前端發送的請求由nginx轉發到后端服務器
好處:
提速:nginx本身可緩存數據
負載均衡:配置多臺服務器,大量請求來臨可均衡分配
保證后端安全:不暴露后端服務真實地址
server{listen 80;server_name localhost;location /api/{proxy_pass http://localhost:8080/admin/; #反向代理}
}
**proxy_pass:**該指令是用來設置代理服務器的地址,可以是主機名稱,IP地址加端口號等形式。
如上代碼的含義是:監聽80端口號, 然后當我們訪問 http://localhost:80/api/…/…這樣的接口的時候,它會通過 location /api/ {} 這樣的反向代理到 http://localhost:8080/admin/上來。
負載均衡
upstream webservers{server 192.168.100.128:8080;server 192.168.100.129:8080;
}
server{listen 80;server_name localhost;location /api/{proxy_pass http://webservers/admin;#負載均衡}
}
upstream:如果代理服務器是一組服務器,可以使用upstream指令配置后端服務器
如上代碼的含義是:監聽80端口號, 然后當我們訪問 http://localhost:80/api/…/…這樣的接口的時候,它會通過 location /api/ {} 這樣的反向代理到 http://webservers/admin,根據webservers名稱找到一組服務器,根據設置的負載均衡策略(默認是輪詢)轉發到具體的服務器。
注:upstream后面的名稱可自定義,但要上下保持一致。
負載策略:
輪詢:默認
upstream webservers{server 192.168.100.128:8080;server 192.168.100.129:8080;
}
weight:添加權重
upstream webservers{server 192.168.100.128:8080 weight=90;server 192.168.100.129:8080 weight=10;
}
配置前端環境
nginx.conf
#user nobody;worker_processes 1;#error_log logs/error.log;#error_log logs/error.log notice;#error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 1024;}http {include mime.types;default_type application/json;sendfile on;keepalive_timeout 65;upstream webservers{server 127.0.0.1:8080 weight=90 ;#server 127.0.0.1:8088 weight=10 ;}server {listen 80;server_name localhost;# 指定前端項目所在的位置location / {root html/sky;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}# location /api {# default_type application/json;# #internal;# keepalive_timeout 30s;# keepalive_requests 1000;# #支持keep-alive# proxy_http_version 1.1;# rewrite /api(/.*) $1 break;# proxy_pass_request_headers on;# #more_clear_input_headers Accept-Encoding;# proxy_next_upstream error timeout;# #proxy_pass http://127.0.0.1:8081;# proxy_pass http://backend;# }# 反向代理,處理管理端發送的請求location /api/ {proxy_pass http://localhost:8080/admin/;#proxy_pass http://webservers/admin/;}# 反向代理,處理用戶端發送的請求location /user/ {proxy_pass http://webservers/user/;}# WebSocket# location /ws/ {# proxy_pass http://webservers/ws/;# proxy_http_version 1.1;# proxy_read_timeout 3600s;# proxy_set_header Upgrade $http_upgrade;# proxy_set_header Connection "$connection_upgrade";# }}# upstream backend {# server 127.0.0.1:8080 weight=90 ;# # server 127.0.0.1:8081 max_fails=5 fail_timeout=10s weight=1;# # server 127.0.0.1:8082 max_fails=5 fail_timeout=10s weight=1;# }}
輸入nginx -s reload重啟服務,訪問80端口有效
項目整體架構:
sky-take-out 父工程,通過pom統一管理依賴版本并聚合其他子模塊
sky-common 存放常量,工具類,異常類等子模塊
sky-pojo 存放實體,VO、DTO等
sky-server 子模塊,存放配置文件、Controller、Service、mapper等配置文件
common:
constant 相關常量
context 上下文
enumeration 枚舉操作
exception 自定義異常
json 處理json轉換 基于jackson將Java對象轉為json,或者將json轉為Java對象
properties 存放springboot相關配置屬性類,如阿里OSS服務,jwt令牌和微信支付接口等配置
result 返回結果封裝,分頁返回類和標準返回格式
Serializable接口是Java提供的一個標記接口,用于表示一個類的對象是否可被序列化,序列化意味著將對象轉換為字節流,還能從字節重構對象,
- 一個類只有實現了 Serializable 接口,它的對象才是可序列化的。因此如果要序列化某些類的對象,這些類就必須實現 Serializable 接口。而實際上,Serializable 的源碼是一個空接口,沒有什么具體內容,它的目的只是簡單的標識一個類的對象可以被序列化。
util 常見工具類
pojo
entity :存放與數據庫表對應的實體
DTO :數據傳輸對象,在后端各層中進行傳輸
VO :視圖,用于為前端展示數據所提供的對象
server
config:配置類
controller
interceptor:攔截器類
mapper:
service:
SkyApplication :啟動類
前后端整體實現思路:
controller層:
@PostMapping("/login")
public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) { log.info("員工登錄:{}", employeeLoginDTO); Employee employee = employeeService.login(employeeLoginDTO); //登錄成功后,生成jwt令牌 Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaimsConstant.EMP_ID, employee.getId()); String token = JwtUtil.createJWT( jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), claims); EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder() .id(employee.getId()) .userName(employee.getUsername()) .name(employee.getName()) .token(token) .build(); return Result.success(employeeLoginVO);
}
1.接受登陸請求:
方法接收了一個EmplyeeLoginDTO類型的參數,這個DTO包括前端傳來的用戶名和密碼,用于員工登陸,@RequestBody注解表示請求體以JSON格式傳遞
2.調用Service層進行驗證
調用emplyeeService的login()方法,將登陸信息傳遞給服務層,服務層若驗證成功會返回對應的Employee實體對象
3.生成JWT Token
使用JwtUtil.createJWT()方法根據員工id生成JWT令牌
令牌包括用戶標識emplyee.getId()
過期時間以及簽名算法
4.構建返回值對象給EmployeeLoginVO
創建EmployeeLoginVO對象,封裝登錄成功后需要返回給前端的信息
包括員工ID,用戶名、姓名,token(用于后續請求的身份憑證)
service層:
public Employee login(EmployeeLoginDTO employeeLoginDTO) { String username = employeeLoginDTO.getUsername(); String password = employeeLoginDTO.getPassword(); //1、根據用戶名查詢數據庫中的數據 Employee employee = employeeMapper.getByUsername(username); //2、處理各種異常情況(用戶名不存在、密碼不對、賬號被鎖定) if (employee == null) { //賬號不存在 throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND); } //密碼比對 // TODO 后期需要進行md5加密,然后再進行比對 if (!password.equals(employee.getPassword())) { //密碼錯誤 throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR); } if (employee.getStatus() == StatusConstant.DISABLE) { //賬號被鎖定 throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED); } //3、返回實體對象 return employee;
}
1.獲取用戶名與密碼
從傳入的DTO中提取用戶名和密碼
2.查詢數據庫中的員工信息
emplyeeMapper.getByUsername(Username),通過MyBatis框架調用數據庫接口
3.異常處理邏輯
異常類型:AccountNofFoundException(用戶名不存在)
4.密碼比對并檢查賬號狀態
5.返回員工實體
所有校驗通過后,返回查詢到的員工對象,該對象將用于生成JWT Token,并構建成功響應
Mapper層
@Select("select * from employee where username = #{username}")
Employee getByUsername(String username);
這部分代碼定義了一個名為getByUsername
的方法,該方法接收一個String
類型的參數username
。@Select
注解表示這是一個用于執行 SQL 查詢的語句,其注解內的 SQL 語句select * from employee where username = #{username}
意思是從名為employee
的表中查詢所有列,條件是username
字段的值等于傳入的username
參數值。方法返回類型為Employee
,即查詢結果會封裝成Employee
對象返回,整體功能是根據給定的用戶名從employee
表中獲取對應的員工信息。
完善登錄功能:
Employee employee = employeeMapper.getByUsername(username);
log.info("password={}",password);
使用MD5加密的方式對明文密碼進行加密得到:
e10adc3949ba59abbe56e057f20f883e
將sql里的admin密碼從123456改為以上
password = DigestUtils.md5DigestAsHex(password.getBytes());if (!password.equals(employee.getPassword())) {//密碼錯誤throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);}
修改代碼后登陸成功
Swagger
用于生成、描述、調用和可視化RESful風格的Web服務,作用:
使得前后端分離開發更加方便
接口文檔自動生成,減輕后端開發人員編寫借口的負擔
可進行功能測試
knife4j是為Java MVC框架集成Swagger生成Api文檔的增強解決方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一樣小巧,輕量,并且功能強悍!
目前,一般都使用knife4j框架。
步驟
1.導入pom坐標
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
2.在配置類中加入knife4j相關配置
WebMvcConfiguration.java
/*** 通過knife4j生成接口文檔* @return
*/@Beanpublic Docket docket() {ApiInfo apiInfo = new ApiInfoBuilder().title("蒼穹外賣項目接口文檔").version("2.0").description("蒼穹外賣項目接口文檔").build();Docket docket = new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller")).paths(PathSelectors.any()).build();return docket;}
3.設置靜態資源映射
WebMvcConfiguration.java
/*** 設置靜態資源映射* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
4.訪問
在http://localhost:8080/doc.html頁面進行訪問
常見注解
@Api 類上,例如Controller,說明類
@ApiModel 用于類,例如entity,DTO,VO
@ApiModelProperty 用于屬性,描述屬性信息
@ApiOperation 用于方法上,說明方法用途、作用
使用上述注解,生成可讀性更好的接口文檔
@ApiModel(description = "員工登錄時傳遞的數據模型")
public class EmployeeLoginDTO implements Serializable @ApiModel(description = "員工登錄返回的數據格式")
public class EmployeeLoginVO implements Serializable@ApiOperation(value = "員工登錄")public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO)
總結
1. Nginx 反向代理的作用是什么?如何配置?
作用:
- 轉發前端請求到后端服務器,隱藏真實地址,保障安全;
- 緩存數據提速,支持負載均衡分配多臺服務器請求。
配置示例:
server {listen 80;server_name localhost;location /api/ {proxy_pass http://localhost:8080/admin/; # 反向代理到后端地址}
}
2. Nginx 負載均衡的策略有哪些?如何配置權重?
常見策略:
- 輪詢(默認):請求依次分配到各服務器;
- weight 權重:按權重比例分配,適用于性能不同的服務器。
權重配置示例:
upstream webservers {server 192.168.100.128:8080 weight=90; # 權重90%server 192.168.100.129:8080 weight=10; # 權重10%
}
3. 蒼穹外賣項目的整體架構如何設計?各模塊的作用是什么?
架構設計:
- sky-take-out:父工程,管理依賴版本并聚合子模塊;
- sky-common:存放常量、工具類、異常類等公共資源;
- sky-pojo:定義實體類(Entity)、數據傳輸對象(DTO)、視圖對象(VO);
- sky-server:核心模塊,包含配置、Controller、Service、Mapper 等業務邏輯。
4. 登錄功能的后端實現流程是什么?如何保證密碼安全?
流程:
- Controller 層:接收前端登錄請求,解析用戶名和密碼;
- Service 層:根據用戶名查詢數據庫,驗證密碼和賬號狀態(是否鎖定);
- 生成 JWT 令牌:登錄成功后,基于員工 ID 生成令牌并返回給前端。
密碼安全:
使用 MD5 加密算法對明文密碼加密
password = DigestUtils.md5DigestAsHex(password.getBytes()); // 加密后比對
5. 什么是 Knife4j?如何配置接口文檔?
Knife4j:
是 Swagger 的增強解決方案,用于自動生成、可視化 RESTful 接口文檔,簡化前后端協作。
配置步驟:
- 導入依賴:
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
- 配置 Docket Bean(如 WebMvcConfiguration):
@Bean
public Docket docket() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(new ApiInfoBuilder().title("接口文檔").build()).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller")).build();
}
- 訪問地址:
http://localhost:8080/doc.html
6. JWT 令牌在登錄功能中的作用是什么?如何生成?
作用:
- 作為用戶身份憑證,避免每次請求都查詢數據庫;
- 實現無狀態認證,減輕服務器負擔。
// 基于員工ID生成JWT令牌
Map<String, Object> claims = new HashMap<>();
claims.put("empId", employee.getId());
String token = JwtUtil.createJWT(secretKey, // 簽名密鑰ttlMillis, // 過期時間claims // 載荷數據
);