Java中Spring Security的使用及最佳實(shí)踐
1. Spring Security 簡介
Spring Security 是一個功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架,它是 Spring 生態(tài)系統(tǒng)中的標(biāo)準(zhǔn)安全框架。主要提供以下功能:
- 認(rèn)證(Authentication):驗(yàn)證用戶身份
- 授權(quán)(Authorization):控制用戶訪問權(quán)限
- 防護(hù)攻擊:防止 CSRF、會話固定等攻擊
- 與其他技術(shù)集成:如 OAuth2、LDAP 等
2. 快速開始
2.1 添加依賴
對于 Maven 項(xiàng)目,在 pom.xml 中添加:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>對于 Gradle 項(xiàng)目:
implementation 'org.springframework.boot:spring-boot-starter-security'
2.2 基本配置
創(chuàng)建一個配置類繼承 WebSecurityConfigurerAdapter:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}3. 詳細(xì)配置
3.1 認(rèn)證配置
內(nèi)存認(rèn)證
WebSecurityConfigurerAdapter.java
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER")
.and()
.withUser("admin").password("{noop}admin").roles("ADMIN");
}JDBC 認(rèn)證(可選)
WebSecurityConfigurerAdapter.java
@Autowired
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username,password,enabled from users where username=?")
.authoritiesByUsernameQuery("select username,authority from authorities where username=?");
}自定義 UserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
getAuthorities(user.getRoles()));
}
private Collection<? extends GrantedAuthority> getAuthorities(Collection<Role> roles) {
return roles.stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toList());
}
}3.2 授權(quán)配置
WebSecurityConfigurerAdapter.java
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.failureUrl("/login?error=true")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout=true")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.rememberMe()
.key("uniqueAndSecret")
.tokenValiditySeconds(86400);
}3.3 密碼加密
推薦使用 BCrypt 密碼編碼器:
WebSecurityConfigurerAdapter.java
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}然后在用戶注冊時加密密碼:
WebSecurityConfigurerAdapter.java
@Autowired
private PasswordEncoder passwordEncoder;
public void registerUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRepository.save(user);
}4. 高級特性
4.1 CSRF 防護(hù)
Spring Security 默認(rèn)啟用 CSRF 防護(hù)。在表單中添加 CSRF 令牌:
<form method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<!-- 其他表單字段 -->
</form>
或者在 AJAX 請求中添加:
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
4.2 方法級安全
啟用方法級安全:
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}
然后在方法上使用注解:
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long userId) {
// ...
}
@PostAuthorize("returnObject.owner == authentication.name")
public Document getDocument(Long docId) {
// ...
}
@Secured("ROLE_ADMIN")
public void updateUser(User user) {
// ...
}4.3 OAuth2 集成
添加依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>配置 OAuth2 客戶端:
spring:
security:
oauth2:
client:
registration:
google:
client-id: your-client-id
client-secret: your-client-secret
scope: email, profile4.4 JWT 集成
添加依賴:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>創(chuàng)建 JWT 工具類:
public class JwtTokenUtil {
private String secret = "your-secret-key";
private long expiration = 86400000; // 24小時
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
// 其他工具方法...
}配置 JWT 過濾器:
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtTokenUtil.getUsernameFromToken(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}在安全配置中添加過濾器:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/authenticate").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}5. 測試安全配置
5.1 測試控制器
@RestController
public class TestController {
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
return "Admin Board";
}
@GetMapping("/user")
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
public String userAccess() {
return "User Content";
}
@GetMapping("/all")
public String allAccess() {
return "Public Content";
}
}5.2 測試安全配置
@SpringBootTest
@AutoConfigureMockMvc
public class SecurityTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser(username = "user", roles = {"USER"})
public void givenUserRole_whenAccessUserEndpoint_thenOk() throws Exception {
mockMvc.perform(get("/user"))
.andExpect(status().isOk());
}
@Test
@WithMockUser(username = "user", roles = {"USER"})
public void givenUserRole_whenAccessAdminEndpoint_thenForbidden() throws Exception {
mockMvc.perform(get("/admin"))
.andExpect(status().isForbidden());
}
@Test
public void givenUnauthenticated_whenAccessPublicEndpoint_thenOk() throws Exception {
mockMvc.perform(get("/all"))
.andExpect(status().isOk());
}
}6. 最佳實(shí)踐
- 最小權(quán)限原則:只授予必要的權(quán)限
- 密碼安全:始終使用強(qiáng)密碼哈希算法(如 BCrypt)
- HTTPS:在生產(chǎn)環(huán)境中強(qiáng)制使用 HTTPS
- 安全頭:啟用安全頭(如 X-Frame-Options, X-XSS-Protection 等)
- 定期更新:保持 Spring Security 版本更新
- 審計日志:記錄重要的安全事件
- 輸入驗(yàn)證:不要依賴 Spring Security 進(jìn)行所有輸入驗(yàn)證
7. 常見問題解決
7.1 自定義登錄頁面
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/perform_login")
.defaultSuccessUrl("/home.html", true)
.failureUrl("/login.html?error=true");
}7.2 處理 AccessDeniedException
創(chuàng)建自定義訪問拒絕處理器:
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.sendRedirect(request.getContextPath() + "/access-denied");
}
}然后在配置中使用:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler);
}
7.3 多 HTTP 安全配置
@Configuration
@Order(1)
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("API_USER")
.and()
.httpBasic();
}
}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}8. 總結(jié)
Spring Security 是一個功能全面且靈活的安全框架,本教程涵蓋了從基礎(chǔ)配置到高級特性的主要內(nèi)容。實(shí)際應(yīng)用中,應(yīng)根據(jù)具體需求選擇合適的認(rèn)證和授權(quán)方式,并遵循安全最佳實(shí)踐。
對于更復(fù)雜的場景,建議參考 Spring Security 官方文檔 和社區(qū)資源。
到此這篇關(guān)于Java中Spring Security的使用的文章就介紹到這了,更多相關(guān)Spring Security使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis-plus插入數(shù)據(jù)遇到主鍵沒有默認(rèn)值的情況
這篇文章主要介紹了Mybatis-plus插入數(shù)據(jù)遇到主鍵沒有默認(rèn)值的情況,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
springboot yml中profiles的巧妙用法(小白必看多環(huán)境配置)
這篇文章主要介紹了springboot yml中profiles的巧妙用法,非常適合多環(huán)境配置場景,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04
Java模擬rank/over函數(shù)實(shí)現(xiàn)獲取分組排名的方法詳解
這篇文章主要為大家詳細(xì)介紹了Java模擬rank()、over()函數(shù)獲取分組排名的方法設(shè)計及實(shí)現(xiàn),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-04-04
springboot啟動bat/bash腳本實(shí)現(xiàn)過程
請?zhí)峁┚唧w錯誤信息或腳本內(nèi)容,以便分析Spring Boot項(xiàng)目中Controller、Entity、Service層的依賴問題,跨平臺腳本需注意路徑兼容性和環(huán)境變量配置,確保依賴項(xiàng)正確引入2025-08-08
SpringBoot使用Docx4j實(shí)現(xiàn)DOCX轉(zhuǎn)PDF功能
在當(dāng)今的企業(yè)級應(yīng)用中,文檔格式轉(zhuǎn)換是一個高頻但又容易被低估的需求,從合同簽署、報表生成到知識庫管理,DOCX轉(zhuǎn)PDF的需求無處不在,今天我們就介紹一款使用純Java實(shí)現(xiàn) DOCX 轉(zhuǎn) PDF的方案,超級簡單,簡直不要太爽,需要的朋友可以參考下2026-02-02
Spring boot 4 搞懂MyBatis-Plus的用法解析
MyBatis-Plus是MyBatis的增強(qiáng)工具,提供了CRUD操作和自動填充等功能,通過繼承BaseMapper接口,可以快速實(shí)現(xiàn)數(shù)據(jù)庫操作,MyBatis-Plus提供了多種插件,如分頁、樂觀鎖、多租戶等,以增強(qiáng)系統(tǒng)的功能和性能,本文介紹Spring boot 4 搞懂MyBatis-Plus的用法,感興趣的朋友一起看看吧2026-01-01
Java中@DateTimeFormat和@JsonFormat注解介紹
@DateTimeFormat和@JsonFormat都是處理時間格式化問題的,把其他類型轉(zhuǎn)換成自己需要的時間類型,下面這篇文章主要給大家介紹了關(guān)于Java中@DateTimeFormat和@JsonFormat注解介紹的相關(guān)資料,需要的朋友可以參考下2022-11-11

