SpringBoot+Mybatis+MySQL+Vue+ElementUI前后端分離版:日志管理(四)集成Spring Security

目錄

一、前言

二、后端開發及調整

1.日志管理開發

2.配置調整

?3.日志入庫(注解、切面)

三、前端調整

1.日志管理開發

四、附:源碼

1.源碼下載地址

五、結語

一、前言

此文章在上次調整的基礎上開發后端管理系統的用戶請求日志功能,并集成了Spring Security用來替代jwt認證和緩存用戶信息,以便于日志能記錄詳細的用戶操作信息。新增日志管理菜單可視化日志信息。

此項目是在我上一個文章的后續開發, 需要的同學可以關注一下,文章鏈接如下:SpringBoot+Mybatis+MySQL+Vue+ElementUI前后端分離版:權限管理(三)

(注:源碼我會在文章結尾提供gitee連接,需要的同學可以去自行下載)

二、后端開發及調整

1.日志管理開發

1.新建用戶操作日志表user_log

CREATE TABLE `user_log` (`id` int NOT NULL AUTO_INCREMENT COMMENT '主鍵',`type` varchar(120) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作類型',`method` varchar(120) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作接口',`status` varchar(120) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作狀態(0成功,1失敗)',`text` text COLLATE utf8mb4_general_ci COMMENT '響應結果',`ip_address` varchar(120) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'ip地址',`user_agent` varchar(120) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '客戶端信息',`user_id` int DEFAULT NULL COMMENT '操作員ID(用戶id)',`create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '創建時間/操作時間',`create_by` varchar(120) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '創建人/操作員',`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',`update_by` varchar(120) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',`del_flag` varchar(120) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '刪除標識0未刪除,1已刪除(邏輯刪除)',`remark` varchar(120) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '備注',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=253 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用戶操作日志表'

2.新增用戶操作日志 實體類UserLogEntity.java


import lombok.Data;/*** 用戶操作日志表* @TableName user_log*/
@Data
public class UserLogEntity extends BaseEntity{/*** 主鍵*/private Integer id;/*** 操作類型*/private String type;/*** 操作接口*/private String method;/*** 操作狀態(0成功,1失敗)*/private String status;/*** 響應結果*/private String text;/*** ip地址*/private String ipAddress;/*** 客戶端信息*/private String userAgent;/*** 操作員ID(用戶id)*/private Integer userId;/*** 備注*/private String remark;
}

3.新增用戶操作日志控制器類LogController.java


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.wal.userdemo.DTO.req.QueryUserLogReq;
import org.wal.userdemo.entity.UserLogEntity;
import org.wal.userdemo.service.UserLogService;
import org.wal.userdemo.utils.Result;import java.util.List;/*** 日志控制器類* 處理用戶日志相關的API請求*/
@RestController
@RequestMapping("/api/log")
public class LogController {@Autowiredprivate UserLogService userLogService;/*** 獲取用戶日志列表* 根據查詢條件獲取用戶日志數據,支持分頁功能** @param queryUserLogReq 用戶日志查詢請求參數對象,包含查詢條件和分頁信息* @return Result 返回封裝的用戶日志列表結果,包含數據列表和總記錄數*/@PostMapping("/getUserLogList")public Result<UserLogEntity> getUserLogList(QueryUserLogReq queryUserLogReq) {// 查詢用戶日志列表數據List<UserLogEntity> userLogList = userLogService.getUserLogList(queryUserLogReq);// 獲取符合條件的用戶日志總記錄數int total = userLogService.getUserLogCount(queryUserLogReq);return Result.page(userLogList, total);}@GetMapping("/getUserLogById")public Result<UserLogEntity> getUserLogById(Integer id) {// 返回用戶日志數據return Result.success(userLogService.getUserLogById(id));}
}

?4.新增用戶操作日志服務類UserLogService.java


import org.wal.userdemo.DTO.req.QueryUserLogReq;
import org.wal.userdemo.entity.UserLogEntity;import java.util.List;public interface UserLogService {int insert(UserLogEntity record);List<UserLogEntity> getUserLogList(QueryUserLogReq queryUserLogReq);int getUserLogCount(QueryUserLogReq queryUserLogReq);UserLogEntity getUserLogById(Integer id);
}

5.新增用戶操作日志實現類UserLogServiceImpl.java


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.wal.userdemo.DTO.req.QueryUserLogReq;
import org.wal.userdemo.entity.UserLogEntity;
import org.wal.userdemo.mapper.UserLogMapper;
import org.wal.userdemo.service.UserLogService;import java.util.Collections;
import java.util.List;@Service
public class UserLogServiceImpl implements UserLogService {@Autowiredprivate UserLogMapper userLogMapper;@Overridepublic int insert(UserLogEntity record) {return userLogMapper.insert( record);}@Overridepublic List<UserLogEntity> getUserLogList(QueryUserLogReq queryUserLogReq) {return userLogMapper.getUserLogList(queryUserLogReq);}@Overridepublic int getUserLogCount(QueryUserLogReq queryUserLogReq) {return userLogMapper.getUserLogCount(queryUserLogReq);}@Overridepublic UserLogEntity getUserLogById(Integer id) {return userLogMapper.getUserLogById(id);}
}

6.新增用戶操作日志Mapper接口類UserLogMapper.java

import org.apache.ibatis.annotations.Mapper;
import org.wal.userdemo.DTO.req.QueryUserLogReq;
import org.wal.userdemo.entity.UserLogEntity;import java.util.List;@Mapper
public interface UserLogMapper {int insert(UserLogEntity record);List<UserLogEntity> getUserLogList(QueryUserLogReq queryUserLogReq);int getUserLogCount(QueryUserLogReq queryUserLogReq);UserLogEntity getUserLogById(Integer id);
}

6.新增用戶操作日志Mapper.xml文件UserLogMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.wal.userdemo.mapper.UserLogMapper"><resultMap id="BaseResultMap" type="org.wal.userdemo.entity.UserLogEntity"><id property="id" column="id" /><result property="type" column="type" /><result property="method" column="method" /><result property="status" column="status" /><result property="text" column="text" /><result property="ipAddress" column="ip_address" /><result property="userAgent" column="user_agent" /><result property="userId" column="user_id" /><result property="createTime" column="create_time" /><result property="createBy" column="create_by" /><result property="updateTime" column="update_time" /><result property="updateBy" column="update_by" /><result property="delFlag" column="del_flag" /></resultMap><sql id="Base_Column_List">id,type,method,status,text,ip_address,user_agent,user_id,create_time,create_by,update_time,update_by,del_flag</sql><insert id="insert" parameterType="org.wal.userdemo.entity.UserLogEntity" >insert into user_log<trim prefix="(" suffix=")" suffixOverrides=","><if test="type != null">type,</if><if test="method != null">method,</if><if test="status != null">status,</if><if test="text != null">text,</if><if test="ipAddress != null">ip_address,</if><if test="userAgent != null">user_agent,</if><if test="userId != null">user_id,</if><if test="createTime != null">create_time,</if><if test="createBy != null">create_by,</if><if test="updateTime != null">update_time,</if><if test="updateBy != null">update_by,</if><if test="delFlag != null">del_flag,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="type != null">#{type},</if><if test="method != null">#{method},</if><if test="status != null">#{status},</if><if test="text != null">#{text},</if><if test="ipAddress != null">#{ipAddress},</if><if test="userAgent != null">#{userAgent},</if><if test="userId != null">#{userId},</if><if test="createTime != null">#{createTime},</if><if test="createBy != null">#{createBy},</if><if test="updateTime != null">#{updateTime},</if><if test="updateBy != null">#{updateBy},</if><if test="delFlag != null">#{delFlag},</if></trim></insert><select id="getUserLogList" resultMap="BaseResultMap">select<include refid="Base_Column_List"/>from user_log<where><if test="type != null">and type = #{type}</if><if test="status != null">and status = #{status}</if><if test="createTime != null">and create_time like CONCAT('%',#{createTime},'%')</if></where></select><select id="getUserLogCount" resultType="java.lang.Integer">select count(1) from user_log<where><if test="type != null">and type = #{type}</if><if test="status != null">and status = #{status}</if><if test="createTime != null">and create_time like CONCAT('%',#{createTime},'%')</if></where></select><select id="getUserLogById" resultMap="BaseResultMap" parameterType="integer">select <include refid="Base_Column_List"/>from user_logwhere id = #{id}</select></mapper>
2.配置調整

1.新增pom.xml依賴,支持SpringSecurity

      <!-- Spring Security --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!--常用工具類 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency>

2.移除JwtInterceptor.java攔截器,改用Spring Security進行認證、在上下文緩存登錄用戶信息。

3.移除WebConfig.java配置的請求攔截器,改用Spring Security的config進行過濾和攔截。

4.新增LoginUser.java類,實現Spring Security的UserDetails,封裝用戶認證和授權信息。


import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.wal.userdemo.entity.UserEntity;import java.util.Collection;
import java.util.Collections;/*** 登錄用戶類,實現Spring Security的UserDetails接口* 用于封裝用戶認證和授權信息*/
public class LoginUser implements UserDetails {private Integer userId;private String username;private String password;/*** 構造函數,根據UserEntity初始化LoginUser* @param user 用戶實體對象,包含用戶的基本信息*/public LoginUser(UserEntity user) {this.userId = user.getId();this.username = user.getName();this.password = user.getPassword();}/*** 獲取用戶ID* @return 用戶ID*/public Integer getUserId() {return userId;}/*** 獲取用戶權限集合* @return 包含用戶權限的集合,默認返回"USER"權限*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return Collections.singletonList(new SimpleGrantedAuthority("USER"));}/*** 獲取用戶密碼* @return 用戶密碼*/@Overridepublic String getPassword() {return password;}/*** 獲取用戶名* @return 用戶名*/@Overridepublic String getUsername() {return username;}// 實現其他UserDetails方法.../*** 判斷賬戶是否未過期* @return true表示賬戶未過期*/@Overridepublic boolean isAccountNonExpired() {return true;}/*** 判斷賬戶是否未鎖定* @return true表示賬戶未鎖定*/@Overridepublic boolean isAccountNonLocked() {return true;}/*** 判斷憑證是否未過期* @return true表示憑證未過期*/@Overridepublic boolean isCredentialsNonExpired() {return true;}/*** 判斷賬戶是否啟用* @return true表示賬戶已啟用*/@Overridepublic boolean isEnabled() {return true;}
}

5. 新增JwtAuthenticationTokenFilter.java ,jwt認證過濾器,驗證jwt令牌的有效性,有效則解析令牌信息中的用戶信息并設置Spring Security的認證上下文。


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.wal.userdemo.entity.UserEntity;
import org.wal.userdemo.mapper.UserMapper;
import org.wal.userdemo.utils.JwtUtil;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** JWT認證過濾器,用于攔截請求并驗證JWT令牌的有效性。* 如果令牌有效,則從令牌中解析用戶信息,并設置Spring Security的認證上下文。*/
@Component
//@Order(Ordered.HIGHEST_PRECEDENCE + 10)
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate JwtUtil jwtUtil;@Autowiredprivate UserMapper userMapper;/*** 執行JWT認證的核心邏輯。* 該方法會在每個HTTP請求中執行一次,檢查請求頭中的Authorization字段是否包含有效的JWT令牌。** @param request  HTTP請求對象* @param response HTTP響應對象* @param chain    過濾器鏈,用于繼續執行后續過濾器* @throws ServletException 當Servlet處理出現異常時拋出* @throws IOException      當IO操作出現異常時拋出*/@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws ServletException, IOException {// 獲取請求頭中的Authorization字段String authHeader = request.getHeader("Authorization");// 判斷是否存在Bearer類型的JWT令牌if (authHeader != null && authHeader.startsWith("Bearer ")) {// 提取JWT令牌(去除Bearer前綴)String token = authHeader.substring(7);// 驗證JWT令牌是否有效if (jwtUtil.validateToken(token)) {try {// 解析JWT令牌中的用戶IDString userId = jwtUtil.parseUserId(token);// 根據用戶ID查詢用戶信息UserEntity user = userMapper.getUserById(Integer.parseInt(userId));// 加載用戶詳細信息UserDetails userDetails = userDetailsService.loadUserByUsername(user.getName());// 創建認證對象UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());// 設置認證詳情authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));// 將認證信息存入安全上下文SecurityContextHolder.getContext().setAuthentication(authentication);} catch (Exception e) {// 記錄JWT驗證過程中的異常信息logger.error("JWT驗證異常:", e);}}}// 繼續執行過濾器鏈chain.doFilter(request, response);}
}

6.新增UserDetailsConfig.java類,實現Spring Security的UserDetailsService,用于加載用戶詳細信息進行認證


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.wal.userdemo.entity.UserEntity;
import org.wal.userdemo.mapper.UserMapper;/*** 用戶詳情配置類,實現Spring Security的UserDetailsService接口* 用于加載用戶詳細信息進行認證*/
@Service
public class UserDetailsConfig implements UserDetailsService {@Autowiredprivate UserMapper userMapper;/*** 根據用戶名加載用戶詳細信息* @param name 用戶名* @return UserDetails 用戶詳細信息對象* @throws UsernameNotFoundException 當用戶不存在時拋出此異常*/@Overridepublic UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {// 通過用戶名查詢用戶信息UserEntity user = userMapper.getUserByName(name);// 如果用戶不存在,拋出用戶名未找到異常if (user == null) {throw new UsernameNotFoundException("用戶不存在");}// 將用戶實體轉換為登錄用戶對象并返回return new LoginUser(user);}
}

7.新增SecurityConfig.java安全配置類,配置用戶認證、JWT過濾等相關安全組件。


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.wal.userdemo.Filter.JwtAuthenticationTokenFilter;/*** Spring Security安全配置類* 配置用戶認證、權限控制、JWT過濾器等安全相關組件*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;/*** 創建密碼編碼器Bean* 使用BCrypt算法對密碼進行加密處理** @return PasswordEncoder 密碼編碼器實例*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** 配置認證管理器構建器* 設置自定義用戶詳情服務和密碼編碼器** @param auth AuthenticationManagerBuilder認證管理器構建器* @throws Exception 配置過程中可能拋出的異常*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}/*** 暴露AuthenticationManager作為Bean* 用于在應用其他地方進行手動認證操作** @return AuthenticationManager 認證管理器實例* @throws Exception 獲取認證管理器時可能拋出的異常*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/*** 配置HTTP安全策略* 包括CSRF禁用、會話管理、URL權限控制和JWT過濾器添加** @param http HttpSecurity HTTP安全配置構建器* @throws Exception 配置過程中可能拋出的異常*/@Overrideprotected void configure(HttpSecurity http) throws Exception {// 禁用CSRF保護,配置無狀態會話管理http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()// 配置URL訪問權限.authorizeRequests()// 登錄接口允許所有人訪問.antMatchers("/api/auth/login").permitAll()// API接口需要認證后訪問.antMatchers("/api/**").authenticated()// 其他所有請求都需要認證.anyRequest().authenticated();// 在用戶名密碼認證過濾器之前添加JWT認證過濾器http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);}
}

8.重寫LoginController.java的登錄接口進行身份驗證

 /*** 用戶登錄接口* 通過用戶名和密碼進行身份驗證,驗證成功后生成JWT token返回** @param request 登錄請求參數對象,包含用戶名和密碼* @return Result<?> 返回登錄結果,成功時返回JWT token,失敗時返回錯誤信息*/@UserLog("登錄接口")@PostMapping("/login")public Result<?> login(@RequestBody LoginReq request) {try {// 使用Spring Security進行用戶身份驗證Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));// 將認證結果存儲到安全上下文中SecurityContextHolder.getContext().setAuthentication(authentication);// 從認證結果中獲取用戶信息String username = authentication.getName();UserEntity user = userService.getUserByName(username);if (user != null) {// 生成JWT token并返回String token = jwtUtil.generateToken(user.getId());return Result.success(token);} else {return Result.error("用戶不存在");}} catch (Exception e) {e.printStackTrace();return Result.error("用戶名或密碼錯誤2");}}

9.重寫JwtUtil.java工具類,固定簽名秘鑰,?新增校驗Jwt令牌方法。


import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;import java.security.Key;
import java.util.Date;
/*** JwtUtil 是一個工具類,用于生成和解析 JWT(JSON Web Token)。* 主要功能包括:* - 生成帶有用戶名和過期時間的 JWT 令牌* - 從 JWT 令牌中解析出用戶名** 注意事項:* - 密鑰(SECRET_KEY)應通過配置文件管理,避免硬編碼* - 過期時間(EXPIRATION)可按業務需求調整*/
@Component
public class JwtUtil {/*** JWT 簽名所使用的密鑰。* 在生產環境中建議使用更安全的方式存儲,如配置中心或環境變量。*/private static final String SECRET = "myFixedSecretKey12345678901234567890";public static final Key SIGNING_KEY = Keys.hmacShaKeyFor(SECRET.getBytes());/*** JWT 令牌的有效期,單位為毫秒。* 當前設置為 24 小時(86,400,000 毫秒)。*/private static final long EXPIRATION = 86400000; // 24小時/*** 生成 JWT 令牌。** @param userId 用戶id,作為 JWT 的 subject 字段* @return 返回生成的 JWT 字符串*/public static String generateToken(Integer userId) {return Jwts.builder()// 設置 JWT 的主題(通常為用戶標識).setSubject(userId.toString())// 設置 JWT 的過期時間.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))// 使用 HS512 算法簽名,并指定密鑰.signWith(SIGNING_KEY)// 構建并返回緊湊格式的 JWT 字符串.compact();}/*** 從 JWT 令牌中解析出用戶ID。** @param token 需要解析的 JWT 字符串* @return 解析出的用戶ID(subject)* @throws JwtException 如果 token 無效或簽名不匹配會拋出異常*/public static String parseUserId(String token) {return Jwts.parser()// 設置簽名驗證所使用的密鑰.setSigningKey(SIGNING_KEY)// 解析并驗證 JWT 令牌.parseClaimsJws(token)// 獲取 JWT 中的負載(claims),并提取 subject(用戶名).getBody().getSubject();}/*** 驗證 JWT 令牌。** @param token 需要驗證的 JWT 令牌* @return 如果令牌有效則返回 true,否則返回 false*/public boolean validateToken(String token) {try {Jwts.parserBuilder().setSigningKey(SIGNING_KEY).build().parseClaimsJws(token);return true;} catch (JwtException | IllegalArgumentException e) {return false;}}}

10. 新增PasswordUtil.java工具類,提供密碼加密和驗證功能。


import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;/*** 密碼工具類* 提供密碼加密和驗證功能*/
@Component
public class PasswordUtil {/*** 密碼編碼器實例* 使用BCrypt算法進行密碼加密*/private static final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();/*** 加密密碼* @param rawPassword 明文密碼* @return 加密后的密碼*/public static String encode(String rawPassword) {return passwordEncoder.encode(rawPassword);}/*** 驗證密碼* @param rawPassword 明文密碼* @param encodedPassword 加密后的密碼* @return 是否匹配*/public static boolean matches(String rawPassword, String encodedPassword) {return passwordEncoder.matches(rawPassword, encodedPassword);}
}

11.新增UserContextUtil.java用戶上下文工具類,提供獲取當前登錄用戶信息的功能。


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.wal.userdemo.config.LoginUser;
import org.wal.userdemo.entity.UserEntity;
import org.wal.userdemo.mapper.UserMapper;/*** 用戶上下文工具類* 提供獲取當前登錄用戶信息的便捷方法*/
@Component
public class UserContextUtil {@Autowiredprivate UserMapper userMapper;/*** 獲取當前登錄用戶信息* 從Spring Security上下文中獲取認證信息,解析出登錄用戶名稱,* 然后通過用戶Mapper查詢完整的用戶實體信息* @return 當前用戶實體,如果未登錄或發生異常則返回null*/public UserEntity getCurrentUser() {try {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && authentication.getPrincipal() instanceof LoginUser) {LoginUser loginUser = (LoginUser) authentication.getPrincipal();return userMapper.getUserByName(loginUser.getUsername());}} catch (Exception e) {// 記錄日志e.printStackTrace();}return null;}/*** 獲取當前登錄用戶名* 通過獲取當前用戶實體來提取用戶名信息* @return 當前用戶名,如果未登錄則返回null*/public String getUsername() {UserEntity user = getCurrentUser();return user != null ? user.getName() : null;}/*** 獲取當前登錄用戶ID* 通過獲取當前用戶實體來提取用戶ID信息* @return 當前用戶ID,如果未登錄則返回null*/public Integer getUserId() {UserEntity user = getCurrentUser();return user != null ? user.getId() : null;}
}

12.可以在任意處調用當前登錄用戶信息,例如,新增用戶、修改菜單等。

    /*** 新增 用戶** @param userEntity* @return Integer*/@Overridepublic Integer addUser(UserEntity userEntity) {userEntity.setCreateTime(DateUtils.getNowDate());userEntity.setCreateBy(userContextUtil.getUsername());// 添加用戶前加密密碼if (null != userEntity.getPassword()  && !"".equals(userEntity.getPassword())) {userEntity.setPassword(PasswordUtil.encode(userEntity.getPassword()));}return userMapper.addUser(userEntity);}
?3.日志入庫(注解、切面)

1.新增UserLog.java自定義用戶操作日志注解。

import java.lang.annotation.*;@Target(ElementType.METHOD) // 注解用于方法
@Retention(RetentionPolicy.RUNTIME) // 注解在運行時有效
@Documented
public @interface UserLog {String value() default "";//操作接口描述
}

2.新增 UserLogAspect.java用戶操作日志切面。

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.wal.userdemo.annotation.UserLog;
import org.wal.userdemo.config.LoginUser;
import org.wal.userdemo.entity.UserEntity;
import org.wal.userdemo.entity.UserLogEntity;
import org.wal.userdemo.mapper.UserMapper;
import org.wal.userdemo.service.UserLogService;
import org.wal.userdemo.utils.DateUtils;
import org.wal.userdemo.utils.JwtUtil;
import org.wal.userdemo.utils.Result;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;/*** 用戶操作日志切面* 攔截帶有 @UserLog 注解的方法,記錄用戶操作日志。* @author wal* @Date 2025年7月23日17:21:06*/
@Slf4j
@Aspect
@Component
public class UserLogAspect {@Autowiredprivate UserLogService userLogService; // 假設有一個服務類用于保存日志@Autowiredprivate UserMapper userMapper;/*** 定義切入點:匹配所有帶有 @UserLog 注解的方法*/@Pointcut("@annotation(org.wal.userdemo.annotation.UserLog)")public void userLog() {}/*** 環繞通知:在目標方法執行前后進行日志記錄* @param joinPoint 連接點對象,包含目標方法的信息* @return 目標方法的返回值* @throws Throwable 目標方法可能拋出的異常*/@Around("userLog()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {// 先執行目標方法Object result = null;Exception exception = null;try {result = joinPoint.proceed();} catch (Exception e) {exception = e;throw e;} finally {// 在 finally 塊中記錄日志,此時認證應該已完成try {recordLog(joinPoint, result, exception);} catch (Exception e) {log.error("記錄操作日志失敗", e);}}return result;}/*** 記錄用戶操作日志的核心邏輯* 提取方法信息、請求信息、用戶信息,并構建 UserLogEntity 對象保存到數據庫* @param joinPoint 連接點對象,包含目標方法的信息* @param result 目標方法的返回結果* @param exception 目標方法拋出的異常(如果有的話)*/private void recordLog(ProceedingJoinPoint joinPoint, Object result, Exception exception) {// 獲取方法簽名MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();// 獲取 @UserLog 注解的值String remark = getUserLogRemark(method);String type = getHttpMethodFromMethod(method);// 獲取請求信息ServletRequestAttributes attributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = null;if (attributes != null) {request = attributes.getRequest();}// 獲取當前用戶信息UserEntity currentUser = getCurrentUser();String methodUrl = null;String ipAddress = null;String userAgent = null;if (request != null) {methodUrl = request.getMethod() + " " + request.getRequestURI();ipAddress = request.getRemoteAddr();userAgent = request.getHeader("User-Agent");}String status = "0";String text = "";if (exception != null) {status = "1";text = "異常信息:" + exception.getMessage();} else if (result instanceof Result<?>) {Result<?> resultObj = (Result<?>) result;int code = resultObj.getCode();status = (code == 200) ? "0" : "1";try {text = new ObjectMapper().writeValueAsString(result);} catch (Exception e) {text = result.toString();}}// 構建并保存日志實體UserLogEntity userLog = new UserLogEntity();userLog.setType(type);userLog.setMethod(methodUrl);userLog.setStatus(status);userLog.setText(text);userLog.setIpAddress(ipAddress);userLog.setUserAgent(userAgent);if (currentUser != null) {userLog.setUserId(currentUser.getId());userLog.setCreateBy(currentUser.getName());userLog.setCreateTime(DateUtils.getNowDate());}userLog.setCreateTime(new Date());userLog.setDelFlag(0);userLog.setRemark(remark);userLogService.insert(userLog);}/*** 獲取 @UserLog 注解中的 remark 值* @param method 目標方法* @return 注解中的值*/private String getUserLogRemark(Method method) {UserLog userLog = method.getAnnotation(UserLog.class);if (userLog != null) {return userLog.value(); // 獲取注解中的值}return ""; // 如果沒有注解,返回空字符串}/*** 獲取當前登錄用戶信息* 支持從 Spring Security 上下文或 JWT Token 中解析用戶信息* @return 當前用戶實體,若未找到則返回 null*/private UserEntity getCurrentUser() {HttpServletRequest request = getCurrentRequest();// 方式1:嘗試從安全上下文獲取UserEntity user = getCurrentUserFromSecurityContext();if (user != null) {return user;}// 方式2:從請求頭的 token 解析if (request != null) {user = getCurrentUserFromToken(request);if (user != null) {return user;}}return null;}/*** 獲取當前 HTTP 請求對象* @return 當前請求對象,若不存在則返回 null*/private HttpServletRequest getCurrentRequest() {ServletRequestAttributes attributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();return attributes != null ? attributes.getRequest() : null;}/*** 從 Spring Security 上下文中獲取當前用戶信息* @return 當前用戶實體,若未找到則返回 null*/private UserEntity getCurrentUserFromSecurityContext() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && authentication.getPrincipal() instanceof LoginUser) {LoginUser loginUser = (LoginUser) authentication.getPrincipal();return userMapper.getUserByName(loginUser.getUsername());}log.error("未找到當前用戶信息");return null;}/*** 根據方法上的注解判斷 HTTP 請求方法類型* @param method 目標方法* @return HTTP 方法類型字符串(如 GET、POST 等)*/public static String getHttpMethodFromMethod(Method method) {if (method.isAnnotationPresent(GetMapping.class)) {return "GET";} else if (method.isAnnotationPresent(PostMapping.class)) {return "POST";} else if (method.isAnnotationPresent(PutMapping.class)) {return "PUT";} else if (method.isAnnotationPresent(DeleteMapping.class)) {return "DELETE";} else if (method.isAnnotationPresent(PatchMapping.class)) {return "PATCH";} else if (method.isAnnotationPresent(RequestMapping.class)) {return "REQUEST";}return "UNKNOWN";}/*** 從請求頭的 JWT Token 中解析當前用戶信息* @param request 當前 HTTP 請求對象* @return 當前用戶實體,若解析失敗或未找到則返回 null*/private UserEntity getCurrentUserFromToken(HttpServletRequest request) {if (request == null) return null;String token = request.getHeader("Authorization");if (token != null && token.startsWith("Bearer ")) {token = token.substring(7);try {String userId = JwtUtil.parseUserId(token);return userMapper.getUserById(Integer.parseInt(userId));} catch (Exception e) {log.warn("解析Token失敗: {}", e.getMessage());return null;}}log.error("未找到Token");return null;}}

3.日志切面實現(以LoginController.java登錄接口為例),在需要實現日志記錄的接口上加上@UserLog(“接口描述”)即可實現訪問該接口時記錄日志。

@UserLog("登錄接口")@PostMapping("/login")public Result<?> login(@RequestBody LoginReq request) {try {// 使用Spring Security進行用戶身份驗證Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));// 將認證結果存儲到安全上下文中SecurityContextHolder.getContext().setAuthentication(authentication);// 從認證結果中獲取用戶信息String username = authentication.getName();UserEntity user = userService.getUserByName(username);if (user != null) {// 生成JWT token并返回String token = jwtUtil.generateToken(user.getId());return Result.success(token);} else {return Result.error("用戶不存在");}} catch (Exception e) {e.printStackTrace();return Result.error("用戶名或密碼錯誤");}}

三、前端調整

1.日志管理開發

1.router路由,新增log.js日志管理=>操作日志

export default [{path: 'log',name: 'log',component: () => import('@/view/logmanage/log.vue'),meta: { title: '操作日志', requiresAuth: true }}]

2.請求封裝,新增log.js封裝日志請求

import request from '@/utils/request';export function getUserLogList(data) {return request({url: '/log/getUserLogList',method: 'post',data: data,});
}
export function getUserLogById(id){return request({url: '/log/getUserLogById',method: 'get',params: {id: id},});
}

?3.在router/index.js中引入路由log.js到index路由下

//省略部分導入import log from './log.js'
Vue.use(Router)const router = new Router({mode: 'history',routes: [{path: '/',name: 'Index',component: Index,redirect: '/login', // 默認重定向到 /homechildren: [{path: '/home',name: 'home',component: () => import('@/view/home.vue'),meta: { title: '首頁', requiresAuth: true }},// 其他子路由也可以放在這里...permission,...log]},
// 其他路由配置、、、、、、、

4.新增log.vue實現用戶操作日志可視化

<template><div><!-- 查詢條件 --><el-form :inline="true" label-position="right" label-width="80px" :model="queryForm"class="query-border-container"><el-row :gutter="20" justify="center"><!-- 操作類型 --><el-col :span="7"><el-form-item label="操作類型"><el-input v-model="queryForm.type" placeholder="請選擇操作類型"></el-input></el-form-item></el-col><!-- 操作狀態 --><el-col :span="7"><el-form-item label="操作狀態"><el-input v-model="queryForm.status" placeholder="請選擇操作狀態"></el-input></el-form-item></el-col><!-- 操作時間 --><el-col :span="7"><el-form-item label="操作時間"><el-date-picker v-model="queryForm.createTime" type="date" placeholder="選擇操作時間"style="width: 100%;"></el-date-picker></el-form-item></el-col><!-- 按鈕組 --><el-col :span="7"><el-form-item><div style="display: flex; gap: 10px;"><el-button type="primary" @click="onQuery" size="small">查詢</el-button><el-button @click="onReset" size="small">重置</el-button></div></el-form-item></el-col></el-row></el-form><!-- 用戶列表 --><el-table :data="tableData" style="width: 100%;" class="table-border-container" max-height="480"v-loading="loading"><el-table-column type="index" label="序號" width="100" align="center"><template #default="scope">{{ (queryForm.page - 1) * queryForm.limit + scope.$index + 1 }}</template></el-table-column><el-table-column prop="type" label="操作類型" width="180" align="center"></el-table-column><el-table-column prop="method" label="操作接口" width="180" align="center"></el-table-column><el-table-column prop="status" label="操作狀態" width="180" align="center"></el-table-column><el-table-column prop="text" label="響應結果" width="180" align="center"><template #default="scope"><el-tooltip :content="scope.row.text" placement="top":disabled="!scope.row.text || scope.row.text.length <= 20"><span class="ellipsis-text">{{ scope.row.text && scope.row.text.length > 20 ?scope.row.text.substring(0, 20) + '...' : scope.row.text }}</span></el-tooltip></template></el-table-column><el-table-column prop="userAgent" label="客戶端信息" width="180" align="center"><template #default="scope"><el-tooltip :content="scope.row.userAgent" placement="top":disabled="!scope.row.userAgent || scope.row.userAgent.length <= 20"><span class="ellipsis-text">{{ scope.row.userAgent && scope.row.userAgent.length > 20 ?scope.row.userAgent.substring(0, 20) + '...' : scope.row.userAgent }}</span></el-tooltip></template></el-table-column><el-table-column prop="createTime" label="操作時間" width="180" align="center"></el-table-column><el-table-column prop="createBy" label="操作用戶" width="180" align="center"></el-table-column><el-table-column label="操作" width="350" align="center"><template #default="scope"><el-button type="primary" size="small" @click="details(scope.$index, scope.row)">詳情</el-button></template></el-table-column></el-table><!-- 分頁 --><el-pagination background layout="total,sizes,prev, pager, next" :total="total" @size-change="handleSizeChange"@current-change="handleCurrentChange" :page-size.sync="queryForm.limit" :page-sizes="[10, 20, 50, 100]"class="page-border-container"></el-pagination></div></template><script>
import { getUserLogList, getUserLogById } from '@/api/logmanage/log';export default {name: 'logView',data() {return {tableData: [],queryForm: {page: 1,limit: 10,type: '',status: '',createTime: '',},total: 0,loading: false,form: {},};},created() {this.getUserLogList();},methods: {// 獲取日志列表getUserLogList() {this.loading = true;getUserLogList(this.queryForm).then(res => {if (res.data.code == 200) {this.tableData = res.data.data;this.total = res.data.total;this.$message.success("獲取日志列表成功!");} else {this.$message.error("獲取日志列表失敗!");}}).finally(() => {this.loading = false;});},// 查詢onQuery() {this.getUserLogList();},// 重置表單并查詢onReset() {this.queryForm = {page: 1,limit: 10,type: '',status: '',createTime: '',};this.getUserLogList();},details(index, row) {getUserLogById(row.id).then(res => {if (res.data.code == 200) {// this.form = res.data.data;// this.showUser = true;} else {this.$message.error("獲取用戶信息失敗!");}});},//分頁器改變handleSizeChange(val) {this.queryForm.limit = val;this.getUserLogList();},//改變頁碼handleCurrentChange(val) {this.queryForm.page = val;this.getUserLogList();},// 取消按鈕:關閉彈窗closeRoleDialog() {this.userId = null;this.selectedRoles = [];this.showRoleDialog = false;},},
};
</script>
<style scoped></style>

四、附:源碼

1.源碼下載地址

https://gitee.com/wangaolin/user-demo.git

同學們有需要可以自行下載查看,此文章是dev-vue分支

五、結語

? ? ? ? 此次開發新引入了Spring Security用來認證和鑒權(沒用到),近期工作較忙,近期不再發版,但是項目我還會繼續維護,確確實實還有多少我覺得不合理、不夠人性化的地方。

后續可能還會加的功能如下:

  • 用戶管理的頭像、郵箱、電話、水印、信息加密等
  • 菜單管理的支持拖拽、過濾等
  • 日志管理細化、分類
  • 完善首頁的數據展示、優化等

有需要的同學可以關注我后續發布的文章和代碼維護。?

(注:接定制化開發前后端分離項目,私我)?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/91840.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/91840.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/91840.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ceph 14.2.22 nautilus Balancer 數據平衡

Ceph Balancer (upmap 模式) 啟用與配置 在 Ceph Nautilus (14.2.22) 版本中啟用和配置 Balancer 的完整步驟 1. 前提檢查 檢查集群的初始狀態和版本。 集群狀態 (ceph -s)cluster:id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxhealth: HEALTH_OKservices:mon: 3 daemons, quo…

在Linux上對固態硬盤進行分區、格式化和掛載的步驟

在Linux上對固態硬盤進行分區、格式化和掛載的步驟如下&#xff1a; 插入固態硬盤&#xff1a;將固態硬盤插入計算機的SATA或M.2接口。 確認固態硬盤被識別&#xff1a;打開終端&#xff0c;輸入以下命令查看硬盤是否被系統識別 fdisk -l 查找硬盤列表中的固態硬盤&#xf…

用Unity結合VCC更改人物模型出現的BUG

1、上傳模型時出現錯誤經過排查是因為服裝發型預制體放到人物模型上之后&#xff0c;物體上自動多了一個空腳本&#xff0c;懷疑是VRC工具箱自動添加的。解決方法&#xff1a;在上傳前將帶有空腳本的物體上的組件刪除即可2、添加頭發時出現模型碰撞錯誤按照【【VRCHAT】從零開始…

k8s之DevicePlugin

解密 Kubernetes Device Plugin&#xff1a;讓容器輕松駕馭特殊硬件 在容器化技術飛速發展的今天&#xff0c;容器憑借輕量、隔離、可移植的特性成為應用部署的主流選擇。但在實際應用中&#xff0c;當容器需要訪問 GPU、FPGA 等特殊硬件資源時&#xff0c;事情就變得不那么簡單…

動態規劃Day7學習心得

今天給動態規劃掃個尾&#xff0c;還有兩題。 第一道&#xff1a;647. 回文子串 - 力扣&#xff08;LeetCode&#xff09; 暴力解法 兩層for循環&#xff0c;遍歷區間起始位置和終止位置&#xff0c;然后還需要一層遍歷判斷這個區間是不是回文。所以時間復雜度&#xff1a;O…

SpringCloud實戰:機器人對戰系統架構

基于Spring Cloud的機器人對戰 以下是基于Spring Cloud的機器人對戰實例相關案例和技術實現方向的整理,涵蓋微服務架構設計、通信機制及典型應用場景: 分布式對戰系統架構 采用Spring Cloud Alibaba+Nacos實現服務注冊與發現,每個機器人實例作為獨立微服務部署。通過Open…

LLM 核心能力解構與項目實踐指南

大語言模型&#xff08;LLM&#xff09;的爆發式發展&#xff0c;本質上是其核心能力在產業場景中的規模化驗證。作為技術博主&#xff0c;本文將系統拆解 LLM 的六大核心能力&#xff0c;結合工業級項目案例&#xff0c;提供從能力映射到工程實現的完整技術路徑&#xff0c;并…

retro-go 1.45 編譯及顯示中文

最近做了個使用 retro-go 的開源掌機 基于ESP32-S3的C19掌機&#xff08;適配GBC外殼&#xff09; - 立創開源硬件平臺 &#xff0c;做完后用提供的固件發現屏幕反顯了&#xff0c;估計是屏幕型號不太對&#xff0c;隨即自己拉 retro-go 官方庫來編譯&#xff0c;拉取的最新的 …

中州養老項目:Mybatis自動填充攔截器

功能:在新增護理項目的時候,創建人,創建時間和修改時間字段會自動攔截填充,這些公共字段可以省去我們一個一個處理的麻煩依靠:AutoFillInterceptor攔截器,MybatisConfig配置類第一步:我們需要借助一個MybatisConfig,configuration標志著這是一個配置類,我們需要將autoFillInter…

[創業之路-527]:什么是產品技術成熟度曲線?

產品技術成熟度曲線&#xff08;Gartner Hype Cycle&#xff09;是由全球知名咨詢機構Gartner提出的工具&#xff0c;用于可視化展示新興技術從誕生到成熟的發展軌跡&#xff0c;以及市場對其預期和實際采用趨勢的變化。該曲線通過五個階段刻畫技術生命周期&#xff0c;幫助企業…

VScode對Ubuntu用root賬號進行SSH遠程連接開發

由于linux服務器大部分都是基于命令行的操作&#xff0c;缺乏比較方便好用的編輯工具&#xff0c;對于經常在linux服務器上做開發的同學來說直接在服務器上進行開發或配置文件的修改還不是特別的方便。雖然linux上有vi或vim比起圖形化的編輯工具體驗感還是不是很好。作為程序員…

【物聯網】基于樹莓派的物聯網開發【20】——樹莓派控制DHT11溫濕度傳感器實戰

傳感器概述 DHT11是一款有已校準數字信號輸出的溫濕度傳感器。 其精度濕度5%RH&#xff0c; 溫度2℃&#xff0c;量程濕度20-90%RH&#xff0c; 溫度0~50℃。分為3個接口&#xff0c;分別為&#xff1a;VCC, DATA, GND。 產品圖片主要用途 檢測環境溫濕度 GPIO控制DHT11溫濕度傳…

AI原生數據庫:告別SQL的新時代來了?

在2025年的今天&#xff0c;生成式AI的浪潮正以前所未有的力量重塑著各行各業。從代碼生成到藝術創作&#xff0c;大型語言模型&#xff08;LLM&#xff09;的能力邊界不斷被拓寬。現在&#xff0c;這股浪潮正涌向信息技術領域最古老、最核心的基石之一&#xff1a;數據庫。一個…

題單【模擬與高精度】

P1042 [NOIP 2003 普及組] 乒乓球 P1042 [NOIP 2003 普及組] 乒乓球 - 洛谷 #include<bits/stdc.h> using namespace std;char C; string S; int n,A,B;void Work(int Lim) {for(char i:S){if(iW) A;if(iL) B;if(max(A,B)>Lim && abs(A-B)>2){cout<<…

數據結構學習基礎和從包裝類緩存到泛型擦除的避坑指南

目錄 1.數據結構的概念和算法 1.1 數據結構的概念 1.2 數據結構的集合框架 1.3 算法 1.3.1 時間復雜度 1.3.2 空間復雜度 2.包裝類 2.1 為什么需要包裝類&#xff1f; 2.2 裝箱和拆箱 3. 初識泛型 3.1 認識泛型 3.2 泛型類的使用 3.3 泛型的編譯 3.4 通配符 3.4.1 …

網絡安全基礎知識【6】

什么是防火墻1.防火墻指的是一個由軟件和硬件設備組合而成、在內部網和外部網之間、 專用網與公共網之間的界面上構造的保護屏障 2.防火墻實際上是一種隔離技術 3.防火墻重要的特征是增加了區域的概念防火墻的定義 隔離可信與不可信網絡的設備/軟件&#xff0c;基于策略控制流量…

Apache Doris數據庫——大數據技術

Apache Doris一、簡介1.1、Apache Doris簡介1.2、Apache Doris 與傳統大數據架構相比1.3、doris是java團隊掌控大數據能力最優選擇1.4、 OLTP&#xff08;在線事務處理&#xff09; 與 OLAP&#xff08;在線分析處理&#xff09;1.5、發展歷程1.6、應用現狀1.7、整體架構1.7.1、…

Conda和pip的使用記錄

Conda和pip的使用記錄一、創建新的 Conda 環境二、激活環境三、安裝其他包&#xff08;可選&#xff09;四、查看已有環境五、刪除環境&#xff08;可選&#xff09;?? Conda 下載緩慢的解決方案&#xff08;推薦使用國內鏡像&#xff09;&#x1f527; 方法一&#xff1a;**…

詳解Python標準庫之互聯網數據處理

詳解Python標準庫之互聯網數據處理 在互聯網時代&#xff0c;數據的產生、傳輸和處理無處不在。從電子郵件的收發到 API 接口的數據交換&#xff0c;從二進制數據的編碼到 MIME 類型的識別&#xff0c;Python 標準庫提供了一整套強大的工具集&#xff0c;幫助開發者輕松應對各種…

適 配 器 模 式

前陣子&#xff0c;筆者在網上淘來一個二手顯示屏來搭配我裝好的主機&#xff0c;但是送到手上后我卻找不到電源適配器的蹤跡。于是我就在家找了根電源線接上了顯示屏&#xff0c;倒是能亮&#xff0c;就是屏幕閃得和機關槍似的。這是因為我的顯示屏需要12V的供電&#xff0c;我…