Spring Security前后端分離接入保姆級教程
基 于 角 色 權(quán) 限 模 型 與 數(shù) 據(jù) 權(quán) 限 控 制
以 小 明 的 攝 影 作 品 管 理 系 統(tǒng) 為 例 , 從 零 基 礎(chǔ) 搭 建 到 完 整 認(rèn) 證 鑒 權(quán) , 逐 步 演 示 Spring Security 的 接 入 流 程 。 涵 蓋 基 礎(chǔ) 接 入 、 角 色 權(quán) 限 擴(kuò) 展 、 數(shù) 據(jù) 權(quán) 限 控 制 三 大 模 塊 , 每 步 標(biāo) 注 自 定 義 類 與 Spring Security 擴(kuò) 展 點 , 配 代 碼 、 注 釋 和 邏 輯 說 明 。 使 用 MyBatisPlus 作 為 ORM 框 架 。
** 第 一 部 分 : 基 礎(chǔ) 接 入 流 程 — 前 后 端 分 離 認(rèn) 證 鑒 權(quán) **
** 第 一 步 : 環(huán) 境 準(zhǔn) 備 與 依 賴 引 入 **
** 目 的 **
搭 建 Spring Boot 基 礎(chǔ) 項 目 , 引 入 Spring Security 及 前 后 端 分 離 必 需 組 件 , 使 用 MyBatisPlus 替 代 JPA 。
** 操 作 **
- ** 創(chuàng) 建 Spring Boot 項 目 **
- 通 過 start.spring.io 創(chuàng) 建 , 選 擇 Maven 、 Java 17+ , 添 加 依 賴 Spring Web 、 Spring Security 、 MyBatis Plus 、 MySQL Driver 、 Lombok 。
- ** Pom.xml 核 心 依 賴 **
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL驅(qū)動 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- JWT支持 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies> ** 第 二 步 : 數(shù) 據(jù) 庫 設(shè) 計 與 實 體 類 **
** 目 的 **
設(shè) 計 用 戶 、 角 色 表 , 并 讓 用 戶 PO 類 實 現(xiàn) Spring Security 的 UserDetails 接 口 。
** 操 作 **
- ** 數(shù) 據(jù) 庫 表 設(shè) 計 **
CREATE TABLE sys_user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE COMMENT '用戶名',
password VARCHAR(100) NOT NULL COMMENT 'BCrypt加密密碼',
email VARCHAR(100) COMMENT '郵箱',
enabled BOOLEAN DEFAULT TRUE COMMENT '賬戶是否啟用'
);
CREATE TABLE sys_role (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
role_name VARCHAR(50) NOT NULL UNIQUE COMMENT '角色名稱(無ROLE_前綴)'
);
CREATE TABLE sys_user_role (
user_id BIGINT,
role_id BIGINT,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES sys_user(id),
FOREIGN KEY (role_id) REFERENCES sys_role(id)
); - ** 實 體 類 UserPo 實 現(xiàn) UserDetails 接 口 **
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
@Data
@TableName("sys_user")
public class UserPo implements UserDetails {
private Long id;
private String username;
private String password;
private String email;
private Boolean enabled = true;
private Set<RolePo> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRoleName()))
.collect(Collectors.toList());
}
@Override
public boolean isAccountNonExpired() { return true; }
@Override
public boolean isAccountNonLocked() { return true; }
@Override
public boolean isCredentialsNonExpired() { return true; }
@Override
public boolean isEnabled() { return enabled; }
} - ** 角 色 實 體 類 RolePo **
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_role")
public class RolePo {
private Long id;
private String roleName;
} ** 第 三 步 : MyBatisPlus Mapper 層 **
** 目 的 **
使 用 MyBatisPlus Mapper 操 作 數(shù) 據(jù) 庫 。
** 操 作 **
- ** UserMapper 接 口 **
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.Optional;
public interface UserMapper extends BaseMapper<UserPo> {
Optional<UserPo> findByUsername(@Param("username") String username);
} - ** RoleMapper 接 口 **
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
public interface RoleMapper extends BaseMapper<RolePo> {
Optional<RolePo> findByRoleName(@Param("roleName") String roleName);
} ** 第 四 步 : Service 層 — 實 現(xiàn) UserDetailsService **
** 目 的 **
實 現(xiàn) Spring Security 的 UserDetailsService 接 口 , 配 置 密 碼 編 碼 器 。
** 操 作 **
- ** 自 定 義 UserDetailsService 實 現(xiàn) 類 **
import lombok.RequiredArgsConstructor;
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;
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserPo user = userMapper.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用戶不存在:" + username));
return user;
}
} - ** 配 置 密 碼 編 碼 器 **
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class PasswordEncoderConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
} ** 第 五 步 : Spring Security 配 置 類 **
** 目 的 **
配 置 認(rèn) 證 規(guī) 則 、 授 權(quán) 規(guī) 則 、 JWT 過 濾 器 。
** 操 作 **
- ** 自 定 義 Security 配 置 類 **
import lombok.RequiredArgsConstructor;
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.configuration.AuthenticationConfiguration;
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.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {
private final UserDetailsServiceImpl userDetailsService;
private final JwtAuthFilter jwtAuthFilter;
private final PasswordEncoder passwordEncoder;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/login", "/api/auth/register").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("ADMIN", "USER")
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
} ** 第 六 步 : JWT 工 具 類 與 過 濾 器 **
** 目 的 **
實 現(xiàn) JWT 生 成 、 驗 證 、 提 取 用 戶 名 , 并 通 過 過 濾 器 轉(zhuǎn) 換 為 認(rèn) 證 信 息 。
** 操 作 **
- ** JWT 工 具 類 JwtUtils **
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.function.Function;
@Component
public class JwtUtils {
@Value("${app.jwt.secret}")
private String secret;
@Value("${app.jwt.expiration}")
private long expiration;
public String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
}
private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
return claimsResolver.apply(claims);
}
private boolean isTokenExpired(String token) {
return extractClaim(token, Claims::getExpiration).before(new Date());
}
} - ** JWT 認(rèn) 證 過 濾 器 JwtAuthFilter **
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtUtils jwtUtils;
private final UserDetailsServiceImpl userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateToken(jwt)) {
String username = jwtUtils.extractUsername(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("無法設(shè)置用戶認(rèn)證: {}", e);
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (headerAuth != null && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7);
}
return null;
}
} ** 第 七 步 : 登 錄 接 口 與 資 源 接 口 **
** 目 的 **
編 寫 登 錄 接 口 認(rèn) 證 并 生 成 Token , 編 寫 受 保 護(hù) 資 源 接 口 。
** 操 作 **
- ** 登 錄 控 制 器 AuthController **
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {
private final AuthenticationManager authenticationManager;
private final JwtUtils jwtUtils;
@PostMapping("/login")
public ResponseEntity<JwtResponse> login(@RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
String jwt = jwtUtils.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(jwt, userDetails.getUsername()));
}
} ** 第 二 部 分 : 權(quán) 限 表 擴(kuò) 展 指 南 — 基 于 角 色 權(quán) 限 模 型 **
** 一 、 數(shù) 據(jù) 庫 擴(kuò) 展 **
** 目 的 **
新 增 權(quán) 限 表 實 現(xiàn) 細(xì) 粒 度 權(quán) 限 控 制 。
** 操 作 **
- ** 新 增 權(quán) 限 表 結(jié) 構(gòu) **
CREATE TABLE sys_permission (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
permission_symbol VARCHAR(100) NOT NULL UNIQUE COMMENT '權(quán)限符號(如 user:add)',
permission_name VARCHAR(100) NOT NULL COMMENT '權(quán)限名稱'
);
CREATE TABLE sys_role_permission (
role_id BIGINT,
permission_id BIGINT,
PRIMARY KEY (role_id, permission_id),
FOREIGN KEY (role_id) REFERENCES sys_role(id),
FOREIGN KEY (permission_id) REFERENCES sys_permission(id)
); ** 二 、 實 體 類 擴(kuò) 展 **
** 目 的 **
修 改 角 色 與 用 戶 實 體 類 關(guān) 聯(lián) 權(quán) 限 。
** 操 作 **
- ** 新 增 權(quán) 限 實 體 類 PermissionPo **
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Set;
@Data
@TableName("sys_permission")
public class PermissionPo {
private Long id;
private String permissionSymbol;
private String permissionName;
private Set<RolePo> roles;
} - ** 修 改 角 色 實 體 類 RolePo **
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Set;
@Data
@TableName("sys_role")
public class RolePo {
private Long id;
private String roleName;
private Set<PermissionPo> permissions;
private Set<UserPo> users;
} - ** 修 改 用 戶 實 體 類 UserPo 權(quán) 限 加 載 **
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authorities = new HashSet<>();
for (RolePo role : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));
for (PermissionPo permission : role.getPermissions()) {
authorities.add(new SimpleGrantedAuthority(permission.getPermissionSymbol()));
}
}
return authorities;
} ** 三 、 MyBatisPlus Mapper 擴(kuò) 展 **
** 目 的 **
添 加 權(quán) 限 相 關(guān) Mapper 接 口 。
** 操 作 **
- ** PermissionMapper **
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.Optional;
public interface PermissionMapper extends BaseMapper<PermissionPo> {
Optional<PermissionPo> findByPermissionSymbol(@Param("symbol") String symbol);
} ** 四 、 Service 層 擴(kuò) 展 **
** 目 的 **
實 現(xiàn) 權(quán) 限 管 理 服 務(wù) 。
** 操 作 **
- ** 權(quán) 限 管 理 服 務(wù) PermissionService **
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class PermissionService {
private final PermissionMapper permissionMapper;
private final RoleMapper roleMapper;
@Transactional
public void assignPermissionToRole(Long roleId, String permissionSymbol) {
RolePo role = roleMapper.selectById(roleId);
PermissionPo permission = permissionMapper.findByPermissionSymbol(permissionSymbol)
.orElseGet(() -> {
PermissionPo p = new PermissionPo();
p.setPermissionSymbol(permissionSymbol);
p.setPermissionName(parsePermissionName(permissionSymbol));
permissionMapper.insert(p);
return p;
});
role.getPermissions().add(permission);
roleMapper.updateById(role);
}
private String parsePermissionName(String symbol) {
return Arrays.stream(symbol.split(":"))
.map(word -> word.substring(0, 1).toUpperCase() + word.substring(1))
.collect(Collectors.joining(" "));
}
} ** 五 、 控 制 器 層 擴(kuò) 展 — 使 用 權(quán) 限 注 解 **
** 目 的 **
通 過 @PreAuthorize 注 解 控 制 方 法 權(quán) 限 。
** 操 作 **
- ** 用 戶 控 制 器 使 用 權(quán) 限 注 解 **
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
@PreAuthorize("hasAuthority('user:add')")
public ResponseEntity<?> addUser() {
return ResponseEntity.ok("添加用戶");
}
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('user:delete')")
public ResponseEntity<?> deleteUser(@PathVariable Long id) {
return ResponseEntity.ok("刪除用戶");
}
} ** 第 三 部 分 : 數(shù) 據(jù) 權(quán) 限 控 制 — 訂 單 查 詢 只 查 看 自 己 創(chuàng) 建 的 訂 單 **
** 一 、 數(shù) 據(jù) 庫 擴(kuò) 展 — 訂 單 表 **
** 目 的 **
設(shè) 計 訂 單 表 關(guān) 聯(lián) 創(chuàng) 建 人 。
** 操 作 **
- ** 訂 單 表 結(jié) 構(gòu) **
CREATE TABLE sys_order (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
order_no VARCHAR(50) NOT NULL COMMENT '訂單編號',
amount DECIMAL(10,2) NOT NULL COMMENT '訂單金額',
creator_id BIGINT NOT NULL COMMENT '創(chuàng)建人ID',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (creator_id) REFERENCES sys_user(id)
); - ** 訂 單 實 體 類 OrderPo **
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@TableName("sys_order")
public class OrderPo {
private Long id;
private String orderNo;
private BigDecimal amount;
private Long creatorId;
private LocalDateTime createdAt;
} ** 二 、 MyBatisPlus Mapper 與 Service **
** 目 的 **
實 現(xiàn) 訂 單 數(shù) 據(jù) 訪 問 與 權(quán) 限 過 濾 。
** 操 作 **
- ** OrderMapper **
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface OrderMapper extends BaseMapper<OrderPo> {
List<OrderPo> findByCreatorId(@Param("creatorId") Long creatorId);
} - ** 訂 單 服 務(wù) 實 現(xiàn) **
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final OrderMapper orderMapper;
private final UserMapper userMapper;
@Override
public List<OrderPo> findMyOrders() {
UserPo currentUser = getCurrentUser();
return orderMapper.findByCreatorId(currentUser.getId());
}
@Override
public OrderPo findOrderById(Long id) {
OrderPo order = orderMapper.selectById(id);
UserPo currentUser = getCurrentUser();
if (!order.getCreatorId().equals(currentUser.getId())) {
throw new AccessDeniedException("無權(quán)訪問此訂單");
}
return order;
}
private UserPo getCurrentUser() {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
return userMapper.findByUsername(username).orElseThrow();
}
} ** 三 、 控 制 器 層 實 現(xiàn) **
** 目 的 **
編 寫 訂 單 相 關(guān) 接 口 , 集 成 數(shù) 據(jù) 權(quán) 限 控 制 。
** 操 作 **
- ** 訂 單 控 制 器 **
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
@GetMapping
@PreAuthorize("hasAuthority('order:view')")
public ResponseEntity<List<OrderPo>> getMyOrders() {
List<OrderPo> orders = orderService.findMyOrders();
return ResponseEntity.ok(orders);
}
@GetMapping("/{id}")
@PreAuthorize("hasAuthority('order:view')")
public ResponseEntity<OrderPo> getOrderById(@PathVariable Long id) {
OrderPo order = orderService.findOrderById(id);
return ResponseEntity.ok(order);
}
} ** 總 結(jié) **
通 過 以 上 三 部 分 整 合 , 我 們 實 現(xiàn) 了 Spring Security 前 后 端 分 離 接 入 、 角 色 權(quán) 限 擴(kuò) 展 、 數(shù) 據(jù) 權(quán) 限 控 制 的 完 整 流 程 。 核 心 要 點 如 下 :
- ** 基 礎(chǔ) 接 入 ** : 實 現(xiàn) 用 戶 認(rèn) 證 與 JWT 授 權(quán) 。
- ** 權(quán) 限 擴(kuò) 展 ** : 通 過 權(quán) 限 表 與 角 色 關(guān) 聯(lián) , 實 現(xiàn) 細(xì) 粒 度 權(quán) 限 控 制 。
- ** 數(shù) 據(jù) 權(quán) 限 ** : 在 Service 層 手 動 過 濾 數(shù) 據(jù) , 確 保 用 戶 只 能 查 看 自 己 創(chuàng) 建 的 訂 單 。
整 個 系 統(tǒng) 兼 顧 了 安 全 性 與 靈 活 性 , 適 用 于 中 小 型 項 目 的 權(quán) 限 管 理 需 求 。
到此這篇關(guān)于Spring Security前后端分離接入流程保姆級教程的文章就介紹到這了,更多相關(guān)Spring Security前后端分離接入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- spring security 超詳細(xì)使用教程及如何接入springboot、前后端分離
- Spring?Security+JWT如何實現(xiàn)前后端分離權(quán)限控制
- 如何使用JWT的SpringSecurity實現(xiàn)前后端分離
- springsecurity實現(xiàn)用戶登錄認(rèn)證快速使用示例代碼(前后端分離項目)
- 詳解SpringSecurity如何實現(xiàn)前后端分離
- SpringSecurity實現(xiàn)前后端分離的示例詳解
- Springboot+Spring Security實現(xiàn)前后端分離登錄認(rèn)證及權(quán)限控制的示例代碼
- SpringSecurity+JWT實現(xiàn)前后端分離的使用詳解
相關(guān)文章
PowerJob的OhMyClassLoader工作流程源碼解讀
這篇文章主要介紹了PowerJob的OhMyClassLoader工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
解決java.sql.SQLException:The?server?time?zone?value?&apo
這篇文章主要介紹了解決java.sql.SQLException:The?server?time?zone?value?'?D1ú±ê×?ê±??'?is?unrecognized問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
Spring Cloud微服務(wù)之間調(diào)用Dubbo的詳細(xì)過程
SpringCloudAlibabaDubbo是阿里巴巴開源的Dubbo與SpringCloud的深度集成方案,本文介紹Spring Cloud微服務(wù)之間調(diào)用Dubbo的詳細(xì)過程,感興趣的朋友跟隨小編一起看看吧2026-02-02
java基于C/S模式實現(xiàn)聊天程序(服務(wù)器)
這篇文章主要為大家詳細(xì)介紹了java基于C/S模式實現(xiàn)聊天程序的服務(wù)器篇,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01
深入了解Java中的過濾器Filter和監(jiān)聽器Listener
這篇文章主要為大家詳細(xì)介紹了Java中的過濾器Filter和監(jiān)聽器Listener的使用以及二者的區(qū)別,文中的示例代碼講解詳細(xì),需要的可以參考一下2022-06-06

