Spring?Security入門(mén)到深入實(shí)戰(zhàn)步驟詳解
小 明 的 攝 影 網(wǎng) 站 已 經(jīng) 用 Spring Boot 搭 建 完 成 , 訪 客 越 來(lái) 越 多 。 他 決 定 給 網(wǎng) 站 加 上 完 整 的 登 錄 認(rèn) 證 鑒 權(quán) 功 能 , 讓 不 同 角 色 的 用 戶 ( 游 客 、 編 輯 、 管 理 員 ) 有 不 同 的 操 作 權(quán) 限 。 今 天 , 他 就 帶 著 這 個(gè) 需 求 , 開(kāi) 啟 Spring Security 的 學(xué) 習(xí) 之 旅 。
** 第 一 步 : 搭 建 Spring Boot 項(xiàng) 目 , 引 入 Spring Security **
** 小 明 的 需 求 **
“ 先 讓 網(wǎng) 站 有 個(gè) 最 基 礎(chǔ) 的 登 錄 功 能 , 能 攔 住 未 登 錄 的 用 戶 。 ”
** 實(shí) 操 步 驟 **
- ** 創(chuàng) 建 Spring Boot 項(xiàng) 目 ** : 用 https://start.spring.io/ , 選
Spring Web( 搭 Web 服 務(wù) ) 、Spring Security( 安 全 框 架 ) 、Spring Data JPA( 操 作 數(shù) 據(jù) 庫(kù) ) 、MySQL Driver( 數(shù) 據(jù) 庫(kù) 驅(qū) 動(dòng) ) 。 - ** 添 加 依 賴 ** ( pom.xml 核 心 部 分 ) :
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId> <!-- Spring Security 依 賴 -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
</dependencies> - ** 運(yùn) 行 項(xiàng) 目 ** : 啟 動(dòng) Spring Boot , 訪 問(wèn)
http://localhost:8080, 會(huì) 自 動(dòng) 跳 轉(zhuǎn) 到 Spring Security 的 默 認(rèn) 登 錄 頁(yè) 。
** 現(xiàn) 象 與 原 理 **
- ** 默 認(rèn) 用 戶 ** : 控 制 臺(tái) 會(huì) 打 印 一 行 密 碼 , 格 式 如
Using generated security password: abcdef12-3456-7890-abcd-ef1234567890, 用 戶 名 固 定 為user。 - ** 原 理 ** : Spring Security 自 動(dòng) 配 置 了 默 認(rèn) 的
InMemoryUserDetailsManager( 內(nèi) 存 用 戶 存 儲(chǔ) ) , 生 成 隨 機(jī) 密 碼 。 所 有 接 口 默 認(rèn) 需 要USER角 色 才 能 訪 問(wèn) 。
?? 小 明 的 疑 問(wèn) : “ 這 密 碼 每 次 啟 動(dòng) 都 變 , 而 且 用 戶 不 能 自 己 注 冊(cè) , 得 改 ! ”
** 第 二 步 : 自 定 義 用 戶 存 儲(chǔ) —— 從 內(nèi) 存 到 數(shù) 據(jù) 庫(kù) **
** 小 明 的 需 求 **
“ 用 戶 信 息 存 在 自 己 的 數(shù) 據(jù) 庫(kù) 里 , 支 持 注 冊(cè) 、 登 錄 , 密 碼 用 BCrypt 加 密 。 ”
** 核 心 概 念 **
- ** UserDetailsService ** : Spring Security 的 “ 用 戶 查 詢 接 口 ” , 開(kāi) 發(fā) 者 實(shí) 現(xiàn) 它 , 告 訴 框 架 “ 怎 么 從 數(shù) 據(jù) 庫(kù) 查 用 戶 ” 。
- ** UserDetails ** : “ 用 戶 信 息 封 裝 類 ” , 包 含 用 戶 名 、 密 碼 、 角 色 、 賬 戶 是 否 過(guò) 期 等 信 息 。
- ** PasswordEncoder ** : “ 密 碼 加 密 器 ” , 用 BCrypt 算 法 把 明 文 密 碼 變 成 哈 希 值 存 儲(chǔ) 。
** 實(shí) 操 步 驟 **
** 建 用 戶 表 ** ( MySQL ) :
CREATE TABLE users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL, -- 用 戶 名
password VARCHAR(100) NOT NULL, -- BCrypt 哈 希 后 的 密 碼
role VARCHAR(20) NOT NULL -- 角 色 : ROLE_VISITOR / ROLE_EDITOR / ROLE_ADMIN
); ** 實(shí) 現(xiàn) UserDetailsService ** ( 查 數(shù) 據(jù) 庫(kù) 用 戶 ) :
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired private UserRepository userRepo; // JPA 操 作 users 表
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1. 查 數(shù) 據(jù) 庫(kù) 用 戶
User user = userRepo.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));
// 2. 封 裝 成 UserDetails 對(duì) 象 ( Spring Security 認(rèn) 識(shí) 的 格 式 )
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword()) // 存 的 是 BCrypt 哈 希 值
.roles(user.getRole().replace("ROLE_", "")) // 角 色 去 掉 ROLE_ 前 綴
.build();
}
} ** 配 置 PasswordEncoder ** ( 用 BCrypt 加 密 ) :
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // BCrypt 加 密 器 , 自 帶 隨 機(jī) 鹽
}
} ** 注 冊(cè) 用 戶 ** ( 比 如 小 明 自 己 注 冊(cè) 為 管 理 員 ) :
@RestController
public class RegisterController {
@Autowired private UserRepository userRepo;
@Autowired private PasswordEncoder passwordEncoder;
@PostMapping("/register")
public String register(String username, String rawPassword, String role) {
User user = new User();
user.setUsername(username);
user.setPassword(passwordEncoder.encode(rawPassword)); // 明 文 密 碼 → BCrypt 哈 希
user.setRole("ROLE_" + role.toUpperCase()); // 角 色 加 ROLE_ 前 綴 ( Spring Security 約 定 )
userRepo.save(user);
return "注冊(cè)成功!";
}
} ** 效 果 **
小 明 用 POST /register?username=xiaoming&rawPassword=123456&role=admin 注 冊(cè) 后 , 數(shù) 據(jù) 庫(kù) users 表 會(huì) 存 一 條 記 錄 , password 字 段 是 BCrypt 哈 希 值 ( 如 $2a$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx )。 登 錄 時(shí) , 框 架 會(huì) 用 BCrypt 驗(yàn) 證 輸 入 密 碼 是 否 匹 配 。
?? 小 明 的 體 會(huì) : “ UserDetailsService 就 像 圖 書(shū) 館 的 ‘ 查 閱 員 ’ , 框 架 告 訴 他 ‘ 找 小 明 ’ , 他 就 去 數(shù) 據(jù) 庫(kù) 把 小 明 的 書(shū) ( 用 戶 信 息 ) 拿 回 來(lái) 。 ”
** 第 三 步 : 配 置 權(quán) 限 控 制 —— 誰(shuí) 能 干 啥 ? **
** 小 明 的 需 求 **
“ 游 客 只 能 看 照 片 , 編 輯 能 上 傳 , 管 理 員 能 刪 除 。 ”
** 核 心 概 念 **
- ** HttpSecurity ** : Spring Security 的 “ Web 安 全 配 置 類 ” , 用 來(lái) 設(shè) 置 URL 訪 問(wèn) 權(quán) 限 、 登 錄 / 注 銷 頁(yè) 、 CSRF 等 。
- ** @PreAuthorize ** : “ 方 法 級(jí) 權(quán) 限 注 解 ” , 直 接 在 Controller 方 法 上 標(biāo) 注 需 要 的 角 色 。
** 實(shí) 操 步 驟 **
** 配 置 URL 訪 問(wèn) 權(quán) 限 ** ( 在 SecurityConfig 中 ) :
@Configuration
@EnableWebSecurity // 開(kāi) 啟 Web 安 全 配 置
public class SecurityConfig {
@Autowired private CustomUserDetailsService userDetailsService;
@Autowired private PasswordEncoder passwordEncoder;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/photos/**").permitAll() // 公 開(kāi) : 所 有 人 能 看 照 片
.requestMatchers("/upload/**").hasRole("EDITOR") // 上 傳 : 需 EDITOR 角 色
.requestMatchers("/delete/**").hasRole("ADMIN") // 刪 除 : 需 ADMIN 角 色
.anyRequest().authenticated() // 其 他 接 口 需 登 錄
)
.formLogin(form -> form // 配 置 登 錄 頁(yè)
.loginPage("/my-login") // 自 定 義 登 錄 頁(yè) ( 下 節(jié) 講 )
.defaultSuccessUrl("/home") // 登 錄 成 功 跳 轉(zhuǎn)
)
.logout(logout -> logout // 配 置 注 銷
.logoutUrl("/logout")
.logoutSuccessUrl("/photos")
);
return http.build();
}
// 配 置 認(rèn) 證 管 理 器 ( 用 自 定 義 UserDetailsService 和 PasswordEncoder )
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
** 用 @PreAuthorize 做 方 法 級(jí) 權(quán) 限 ** ( 更 精 細(xì) 控 制 ) :
@RestController
public class PhotoController {
// 游 客 能 看
@GetMapping("/photos")
public List<Photo> listPhotos() { ... }
// 編 輯 能 上 傳 ( @PreAuthorize 需 開(kāi) 啟 @EnableMethodSecurity )
@PostMapping("/upload")
@PreAuthorize("hasRole('EDITOR')") // 直 接 標(biāo) 注 需 EDITOR 角 色
public String uploadPhoto(MultipartFile file) { ... }
// 管 理 員 能 刪 除
@DeleteMapping("/delete/{id}")
@PreAuthorize("hasRole('ADMIN') or hasRole('EDITOR')") // 管 理 員 或 編 輯 都 能 刪
public String deletePhoto(@PathVariable Long id) { ... }
} - ** 開(kāi) 啟 @PreAuthorize 支 持 ** : 在 SecurityConfig 上 加
@EnableMethodSecurity。
** 效 果 **
- 訪 問(wèn)
/upload時(shí) , 若 用 戶 是ROLE_VISITOR, 會(huì) 被 攔 截 并 返 回 403 錯(cuò) 誤 ( 權(quán) 限 不 足 ) ; - 用
@PreAuthorize的 方 法 , 框 架 會(huì) 在 執(zhí) 行 前 檢 查 用 戶 角 色 , 不 符 合 則 拋 異 常 。
?? 小 明 的 體 會(huì) : “ HttpSecurity 像 ‘ 門(mén) 衛(wèi) ’ , 管 URL 級(jí) 的 大 門(mén) ; @PreAuthorize 像 ‘ 房 間 鎖 ’ , 管 方 法 級(jí) 的 小 門(mén) , 雙 保 險(xiǎn) ! ”
** 第 四 步 : 自 定 義 登 錄 頁(yè) 、 注 銷 與 記 住 我 **
** 小 明 的 需 求 **
“ 默 認(rèn) 登 錄 頁(yè) 太 丑 , 想 用 自 己 的 ; 加 個(gè) ‘ 記 住 我 ’ 功 能 , 關(guān) 閉 瀏 覽 器 再 打 開(kāi) 還 是 登 錄 狀 態(tài) 。 ”
** 實(shí) 操 步 驟 **
- ** 自 定 義 登 錄 頁(yè) ** :
- 寫(xiě) 一 個(gè) HTML 頁(yè) 面
my-login.html( 放resources/templates目 錄 ) , 包 含username、password輸 入 框 和remember-me復(fù) 選 框 ; - 在 SecurityConfig 中 配 置
.loginPage("/my-login"), 并 指 定 處 理 登 錄 的 URL :
.formLogin(form -> form .loginPage("/my-login") // 自 定 義 登 錄 頁(yè) 路 徑 .loginProcessingUrl("/do-login") // 處 理 登 錄 的 URL ( 框 架 自 動(dòng) 處 理 ) .defaultSuccessUrl("/home") .failureUrl("/my-login?error") // 登 錄 失 敗 跳 轉(zhuǎn) ) - 寫(xiě) 一 個(gè) HTML 頁(yè) 面
- ** 開(kāi) 啟 “ 記 住 我 ” 功 能 ** :
- 在 SecurityConfig 中 配 置
.rememberMe():
.rememberMe(remember -> remember .key("xiaoming-secret-key") // 自 定 義 密 鑰 ( 防 篡 改 ) .tokenValiditySeconds(7 * 24 * 3600) // 記 住 我 有 效 期 : 7 天 )- 在 自 定 義 登 錄 頁(yè) 加 “ 記 住 我 ” 復(fù) 選 框 :
<input type="checkbox" name="remember-me"> 記住我
- 在 SecurityConfig 中 配 置
- ** 自 定 義 注 銷 頁(yè) ** :
- 在 SecurityConfig 中 配 置
.logout(), 默 認(rèn) 注 銷 URL 是/logout, 可 自 定 義 :
.logout(logout -> logout .logoutUrl("/my-logout") // 自 定 義 注 銷 URL .logoutSuccessUrl("/photos") // 注 銷 成 功 跳 轉(zhuǎn) .invalidateHttpSession(true) // 注 銷 時(shí) 清 空 Session .deleteCookies("JSESSIONID") // 刪 除 Cookie ) - 在 SecurityConfig 中 配 置
** 效 果 **
- 訪 問(wèn) 需 登 錄 的 頁(yè) 面 時(shí) , 跳 轉(zhuǎn) 到 小 明 自 己 寫(xiě) 的
my-login.html; - 勾 選 “ 記 住 我 ” 后 , 關(guān) 閉 瀏 覽 器 , 7 天 內(nèi) 再 訪 問(wèn) 網(wǎng) 站 仍 是 登 錄 狀 態(tài) ;
- 點(diǎn) 擊 注 銷 鏈 接 , 清 空 Session 和 Cookie , 跳 回 公 開(kāi) 照 片 頁(yè) 。
?? 小 明 的 體 會(huì) : “ 自 定 義 登 錄 頁(yè) 讓 網(wǎng) 站 更 好 看 , ‘ 記 住 我 ’ 像 ‘ 長(zhǎng) 期 飯 卡 ’ , 不 用 每 次 都 刷 臨 時(shí) 卡 。 ”
** 第 五 步 : 集 成 JWT —— 支 持 APP 與 微 服 務(wù) **
** 小 明 的 需 求 **
“ 我 做 了 個(gè) 手 機(jī) APP , 用 Session - Cookie 不 方 便 , 想 用 JWT Token 做 認(rèn) 證 。 ”
** 核 心 概 念 **
- ** JWT ** : “ 自 帶 信 息 的 身 份 證 ” , 服 務(wù) 器 不 存 Session , 用 戶 登 錄 后 拿 Token , 后 續(xù) 請(qǐng) 求 帶 Token 即 可 。
- ** JwtAuthenticationFilter ** : 自 定 義 過(guò) 濾 器 , 從
Authorization頭 取 JWT , 驗(yàn) 證 后 設(shè) 置 Security Context 。
** 實(shí) 操 步 驟 **
- ** 添 加 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> - ** 寫(xiě) JWT 工 具 類 ** ( 生 成 / 驗(yàn) 證 Token ) :
@Component public class JwtUtil { @Value("${jwt.secret}") private String secret; // 配 置 在 application.properties 中 @Value("${jwt.expiration}") private long expiration; // Token 有 效 期 ( 如 3600000 毫 秒 ) // 生 成 Token public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); claims.put("roles", userDetails.getAuthorities().stream() .map(GrantedAuthority::getAuthority).collect(Collectors.toList())); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expiration)) .signWith(SignatureAlgorithm.HS512, secret) // 用 HS512 算 法 簽 名 .compact(); } // 驗(yàn) 證 Token 并 取 用 戶 名 public String getUsernameFromToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject(); } } - ** 寫(xiě) JWT 認(rèn) 證 過(guò) 濾 器 ** :
public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUtil jwtUtil; @Autowired private CustomUserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { // 1. 從 Header 取 Token String token = request.getHeader("Authorization"); if (token != null && token.startsWith("Bearer ")) { token = token.substring(7); // 2. 驗(yàn) 證 Token , 取 用 戶 名 String username = jwtUtil.getUsernameFromToken(token); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { // 3. 查 用 戶 信 息 , 設(shè) 置 Security Context UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(auth); } } chain.doFilter(request, response); // 繼 續(xù) 處 理 請(qǐng) 求 } } - ** 修 改 SecurityConfig , 用 JWT 替 換 Session ** :
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) // JWT 無(wú) 狀 態(tài) , 關(guān) 閉 CSRF .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 無(wú) Session .authorizeHttpRequests(auth -> auth .requestMatchers("/api/photos/**").permitAll() .requestMatchers("/api/upload/**").hasRole("EDITOR") .anyRequest().authenticated() ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) // 加 JWT 過(guò) 濾 器 .formLogin(form -> form.disable()) // 禁 用 默 認(rèn) 表 單 登 錄 ( APP 用 JWT ) .logout(logout -> logout.disable()); // 禁 用 默 認(rèn) 注 銷 return http.build(); }
** 效 果 **
- APP 用 戶 登 錄 時(shí) , 調(diào)
POST /api/login( 自 定 義 接 口 , 驗(yàn) 證 用 戶 名 密 碼 后 返 JWT ) ; - 后 續(xù) APP 請(qǐng) 求 API 時(shí) , 在
Authorization頭 帶Bearer <JWT>, 框 架 自 動(dòng) 認(rèn) 證 。
?? 小 明 的 體 會(huì) : “ JWT 像 ‘ 電 子 通 行 證 ’ , APP 拿 著 它 就 能 暢 通 無(wú) 阻 , 服 務(wù) 器 不 用 記 住 誰(shuí) 來(lái) 過(guò) , 輕 松 支 持 多 端 ! ”
** 第 六 步 : 深 入 : OAuth2 第 三 方 登 錄 與 安 全 防 護(hù) **
** 小 明 的 需 求 **
“ 想 讓 用 戶 用 GitHub 賬 號(hào) 直 接 登 錄 , 還 要 防 CSRF 、 XSS 攻 擊 。 ”
** 核 心 概 念 **
- ** OAuth2 ** : “ 第 三 方 登 錄 協(xié) 議 ” , 用 戶 授 權(quán) GitHub 告 訴 小 明 的 網(wǎng) 站 “ 這 是 我 ” , 無(wú) 需 注 冊(cè) 。
- ** Spring Security OAuth2 Client ** : 框 架 內(nèi) 置 OAuth2 客 戶 端 , 支 持 GitHub 、 Google 等 登 錄 。
** 實(shí) 操 步 驟 **
- ** 配 置 GitHub OAuth2 登 錄 ** :
- 在 GitHub 開(kāi) 發(fā) 者 設(shè) 置 中 創(chuàng) 建 OAuth App , 獲 取
client-id和client-secret; - 在
application.properties中 配 置 :
spring.security.oauth2.client.registration.github.client-id=你的client-id spring.security.oauth2.client.registration.github.client-secret=你的client-secret spring.security.oauth2.client.registration.github.scope=user:email # 申 請(qǐng) 郵 箱 權(quán) 限
- 在 SecurityConfig 中 開(kāi) 啟 OAuth2 登 錄 :
.oauth2Login(oauth2 -> oauth2 .loginPage("/my-login") // 自 定 義 登 錄 頁(yè) 加 “ GitHub 登 錄 ” 按 鈕 .defaultSuccessUrl("/home") ) - 在 GitHub 開(kāi) 發(fā) 者 設(shè) 置 中 創(chuàng) 建 OAuth App , 獲 取
- ** 安 全 防 護(hù) ** ( 框 架 默 認(rèn) 開(kāi) 啟 , 無(wú) 需 額 外 配 置 ) :
- ** CSRF 防 護(hù) ** : 默 認(rèn) 開(kāi) 啟 , 框 架 自 動(dòng) 生 成 CSRF 令 牌 , 表 單 提 交 時(shí) 驗(yàn) 證 ;
- ** XSS 防 護(hù) ** : Spring Boot 默 認(rèn) 開(kāi) 啟 HTML 轉(zhuǎn) 義 , 防 注 入 腳 本 ;
- ** 會(huì) 話 固 定 攻 擊 防 護(hù) ** : 登 錄 后 自 動(dòng) 更 換 SessionID 。
** 效 果 **
- 小 明 的 登 錄 頁(yè) 多 了 個(gè) “ 用 GitHub 登 錄 ” 按 鈕 , 點(diǎn) 擊 后 跳 轉(zhuǎn) GitHub 授 權(quán) , 授 權(quán) 成 功 后 自 動(dòng) 登 錄 網(wǎng) 站 ;
- 框 架 自 動(dòng) 擋 住 CSRF 攻 擊 , 即 使 黑 客 誘 騙 小 明 點(diǎn) 鏈 接 , 也 無(wú) 法 操 作 。
?? 小 明 的 體 會(huì) : “ OAuth2 像 ‘ 找 朋 友 作 證 ’ , 讓 GitHub 幫 忙 證 明 ‘ 這 是 小 明 ’ ; 框 架 的 安 全 防 護(hù) 像 ‘ 隱 形 鎧 甲 ’ , 平 時(shí) 感 覺(jué) 不 到 , 關(guān) 鍵 時(shí) 刻 能 擋 刀 。 ”
** 小 明 的 學(xué) 習(xí) 總 結(jié) **
通 過(guò) 這 幾 步 , 小 明 徹 底 掌 握 了 Spring Security 配 Spring Boot 的 核 心 用 法 :
- ** 從 默 認(rèn) 配 置 入 門(mén) ** , 觀 察 框 架 自 動(dòng) 生 成 的 登 錄 / 權(quán) 限 邏 輯 ;
- ** 自 定 義 UserDetailsService 和 PasswordEncoder ** , 連 接 自 己 的 數(shù) 據(jù) 庫(kù) , 用 BCrypt 加 密 密 碼 ;
- ** 用 HttpSecurity 和 @PreAuthorize 配 置 權(quán) 限 ** , 實(shí) 現(xiàn) URL 級(jí) 和 方 法 級(jí) 控 制 ;
- ** 自 定 義 登 錄 / 注 銷 頁(yè) 、 加 “ 記 住 我 ” ** , 優(yōu) 化 用 戶 體 驗(yàn) ;
- ** 集 成 JWT ** , 支 持 APP 和 微 服 務(wù) 無(wú) 狀 態(tài) 認(rèn) 證 ;
- ** 用 OAuth2 實(shí) 現(xiàn) 第 三 方 登 錄 ** , 開(kāi) 啟 框 架 默 認(rèn) 安 全 防 護(hù) 。
? 小 明 的 最 終 感 悟 :
Spring Security 不 是 一 堆 復(fù) 雜 的 配 置 , 而 是 一 個(gè) “ 安 全 積 木 盒 ” 。 小 明 用 它 搭 建 了 攝 影 網(wǎng) 站 的 安 全 堡 壘 , 從 默 認(rèn) 配 置 到 自 定 義 擴(kuò) 展 , 從 Web 到 APP , 從 賬 號(hào) 密 碼 到 第 三 方 登 錄 , 每 一 步 都 踩 在 “ 需 求 ” 和 “ 技 術(shù) ” 的 結(jié) 合 點(diǎn) 上 。 現(xiàn) 在 , 他 可 以 專 心 拍 攝 更 美 的 照 片 , 而 網(wǎng) 站 的 安 全 , 交 給 Spring Security 就 夠 了 !
到此這篇關(guān)于Spring Security入門(mén)到深入實(shí)戰(zhàn)步驟詳解的文章就介紹到這了,更多相關(guān)Spring Security入門(mén)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring?Security入門(mén)到深入實(shí)戰(zhàn)步驟詳解
- SpringBoot3整合SpringSecurity6快速入門(mén)示例教程
- 深入淺析springsecurity入門(mén)登錄授權(quán)
- SpringSecurity入門(mén)使用教程
- Spring Security十分鐘入門(mén)教程
- SpringBoot?Security從入門(mén)到實(shí)戰(zhàn)示例教程
- java編程SpringSecurity入門(mén)原理及應(yīng)用簡(jiǎn)介
- Java SpringSecurity入門(mén)案例與基本原理詳解
- Spring Security入門(mén)demo案例
- 手把手帶你入門(mén) Spring Security的具體流程
- 淺談spring security入門(mén)
相關(guān)文章
MyBatis-Plus?Page?分頁(yè)不生效的問(wèn)題解決
分頁(yè)是常見(jiàn)的一種功能,本文主要介紹了MyBatis-Plus?Page分頁(yè)不生效的問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07
jxl 導(dǎo)出數(shù)據(jù)到excel的實(shí)例講解
下面小編就為大家分享一篇jxl 導(dǎo)出數(shù)據(jù)到excel的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
Java Robot應(yīng)用示例之機(jī)器人功能
這篇文章主要為大家詳細(xì)介紹了Java Robot應(yīng)用示例之機(jī)器人功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
Java中設(shè)置session超時(shí)(失效)的三種方法
這篇文章主要介紹了Java中設(shè)置session超時(shí)(失效)的三種方法,本文講解了在web容器中設(shè)置、在工程的web.xml中設(shè)置、通過(guò)java代碼設(shè)置3種方法,需要的朋友可以參考下2015-07-07
SSH框架網(wǎng)上商城項(xiàng)目第9戰(zhàn)之添加和更新商品類別功能實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第9戰(zhàn)之添加和更新商品類別功能實(shí)現(xiàn),感興趣的小伙伴們可以參考一下2016-06-06
IntelliJ IDEA下Maven創(chuàng)建Scala項(xiàng)目的方法步驟
這篇文章主要介紹了IntelliJ IDEA下Maven創(chuàng)建Scala項(xiàng)目的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
Java通過(guò)JNI調(diào)用C++動(dòng)態(tài)庫(kù)的完整流程詳解
JNI(Java Native Interface),是實(shí)現(xiàn)Java/Kotlin與C/C++語(yǔ)言之間交互的橋梁,本文主要為大家介紹了Java通過(guò)JNI調(diào)用C++動(dòng)態(tài)庫(kù)的完整流程,需要的可以參考下2025-04-04
spring注解 @PropertySource配置數(shù)據(jù)源全流程
這篇文章主要介紹了spring注解 @PropertySource配置數(shù)據(jù)源全流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03

