自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

基于國密SM4實現(xiàn)用戶認證&授權(quán)

安全 應(yīng)用安全
Spring Security的PasswordEncoder是用于進行密碼加密和驗證的接口。它是一個密碼編碼器,用于將用戶的原始密碼轉(zhuǎn)換為安全的加密字符串,并在驗證過程中將加密后的密碼與用戶提供的密碼進行比較。

JWT生成和認證的基本流程

JWT(JSON Web Token),是目前比較流行的用戶身份驗證解決方案。 下面是一個簡化的時序圖,用于說明JWT生成和認證的基本流程。

圖片圖片

引入 Spring Security 依賴

Spring Security 是一個功能強大且高度可定制的安全框架。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

配置說明

在 Spring Security 配置文件中,我們通常需要做如下配置:

  • AuthenticationProvider實現(xiàn)類:用于自定義身份驗證邏輯;
  • Filter:用于驗證 token 有效性;
  • AuthenticationManager:用于接收并處理身份驗證請求;
  • PasswordEncoder:用于密碼加密和驗證;
  • SecurityFilterChain:過濾器鏈;

圖片圖片

自定義PasswordEncoder

Spring Security的PasswordEncoder是用于進行密碼加密和驗證的接口。它是一個密碼編碼器,用于將用戶的原始密碼轉(zhuǎn)換為安全的加密字符串,并在驗證過程中將加密后的密碼與用戶提供的密碼進行比較。PasswordEncoder接口的主要用于提供安全的密碼存儲和驗證機制,以防止用戶密碼泄露時被惡意使用。它是一種重要的安全性措施,用于保護用戶密碼的安全性。

Spring Security 提供了多種PasswordEncoder接口的實現(xiàn)類,包括:

  • BCryptPasswordEncoder:使用BCrypt算法進行密碼哈希和驗證。它是目前廣泛使用的密碼哈希算法之一,具有較高的安全性。
  • NoOpPasswordEncoder:不進行任何密碼編碼和哈希操作,即明文存儲密碼。不推薦在生產(chǎn)環(huán)境中使用,僅用于測試目的。
  • Pbkdf2PasswordEncoder:使用PBKDF2算法進行密碼哈希和驗證。它通過應(yīng)用哈希函數(shù)多次迭代和鹽值,增加了密碼破解的難度。
  • MessageDigestPasswordEncoder:使用指定的消息摘要算法(如MD5、SHA-1、SHA-256等)進行密碼哈希和驗證。

使用國密(SM4)算法

<!-- SM4依賴 -->
<dependency>
  <groupId>org.bouncycastle</groupId>
  <artifactId>bcprov-jdk15to18</artifactId>
  <version>1.71</version>
</dependency>

自定義的 PasswordEncoder

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.SmUtil;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.nio.charset.StandardCharsets;
import java.util.Objects;

public class Sm4PasswordEncoder implements PasswordEncoder {

 // key長度必須為16
 private static final String KEY = "KeyMustBe16Size.";

 @Override
 public String encode(CharSequence rawPassword) {
return SmUtil.sm4(KEY.getBytes(StandardCharsets.UTF_8)).encryptHex(rawPassword.toString());
 }

 @Override
 public boolean matches(CharSequence rawPassword, String encodedPassword) {
return Objects.equals(rawPassword.toString(),
   SmUtil.sm4(KEY.getBytes(StandardCharsets.UTF_8)).decryptStr(encodedPassword, StandardCharsets.UTF_8));
 }
}

需要實現(xiàn)PasswordEncoder接口的encode()和matches()方法。encode()方法用于對明文密碼進行加密處理,matches()方法用于比較明文密碼與加密后的密碼是否匹配。

配置自定義的 PasswordEncoder

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
 // 其它代碼
 /**
  * 密碼加密方式配置
  */
 @Bean
 public PasswordEncoder passwordEncoder() {
  return new Sm4PasswordEncoder();
 }
}

自定義 Filter 驗證 token 有效性

實現(xiàn)UserDetailsService接口,用于獲取用戶詳細信息

import org.yian.springboot.demo.entity.User;
import org.yian.springboot.demo.service.RoleService;
import org.yian.springboot.demo.service.UserService;
import org.yian.springboot.demo.security.model.AuthUser;
import org.yian.springboot.demo.util.WebUtil;
import lombok.AllArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

 @Resource
 private UserService userService;
 @Resource
 private RoleService roleService;

 @Override
 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  User user = userService.findByUsername(username);
if (Objects.isNull(user)) {
   throw new UsernameNotFoundException("用戶名或密碼錯誤!");
  }
  List<String> roleCodeList = roleService.findRoleCodesByUsername(username);
  List<GrantedAuthority> authorities = roleCodeList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
return new AuthUser(user.getId(), user.getRealName(), user.getAvatar(), user.getPhone(),
   user.getUsername(), user.getPassword(), authorities);
 }
}

UserDetailsServiceImpl類實現(xiàn)了UserDetailsService接口,重寫了loadUserByUsername方法,用于獲取用戶的詳細信息。

圖片圖片

其中AuthUser為自定義認證用戶信息類,代碼如下:

import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.Collection;

@Getter
public class AuthUser extends User {
 /**
  * 用戶ID
  */
 private final String userId;
 /**
  * 真實姓名
  */
 private final String realName;
 /**
  * 電話
  */
 private final String phone;
 /**
  * 頭像
  */
 private final String avatar;

 public AuthUser(String userId, String realName, String avatar, String phone, String username, String password,
     Collection<? extends GrantedAuthority> authorities) {
  super(username, password, true, true, true, true, authorities);
  this.userId = userId;
  this.realName = realName;
  this.avatar = avatar;
  this.phone = phone;
 }
}

AuthUser繼承org.springframework.security.core.userdetails.User,添加了一些業(yè)務(wù)屬性。

自定義 Filter 驗證 token 有效性:

import org.yian.springboot.demo.constant.AuthConstant;
import org.yian.springboot.demo.util.JwtUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 驗證token有效性
 */
@Slf4j
public class TokenFilter extends OncePerRequestFilter {

 @Resource
 private UserDetailsService userDetailsService;

 @Override
 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  String token = getToken(request);
if (StrUtil.isNotEmpty(token)) {
   // 從Token中獲取username
   String username = JwtUtil.getUsernameFromToken(token);
   // 根據(jù)username獲取用戶信息
   UserDetails userDetails = userDetailsService.loadUserByUsername(username);
   // 創(chuàng)建身份驗證對象
   Authentication authentication
    = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
   // 設(shè)置身份驗證對象
   SecurityContextHolder.getContext().setAuthentication(authentication);
  }
  // 過濾器鏈
  filterChain.doFilter(request, response);
 }

 private String getToken(HttpServletRequest request) {
  String bearerToken = request.getHeader("Authorization");
if (StrUtil.isNotEmpty(bearerToken) && bearerToken.startsWith(AuthConstant.AUTHORIZATION_BEARER)) {
   // 去掉令牌前綴
   return bearerToken.replace(AuthConstant.AUTHORIZATION_BEARER, StrUtil.EMPTY);
  }
return null;
 }
}

圖片圖片

配置自定義的自定義Filter:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
 // 其它代碼
 @Bean
 public TokenFilter tokenFilter() {
  return new TokenFilter();
 }
}

配置 AuthenticationProvider

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

 // 其它代碼

 @Resource
 private UserDetailsServiceImpl userDetailsService;

 @Bean
 public DaoAuthenticationProvider authenticationProvider() {
  DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
  authProvider.setUserDetailsService(userDetailsService);
  authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
 }
}

DaoAuthenticationProvider是Spring Security提供的一個身份驗證實現(xiàn)類,它使用數(shù)據(jù)庫中的用戶詳細信息和密碼加密器進行身份驗證。

配置 AuthenticationManager

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

 // 其它代碼

 @Bean
 public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
  return authConfig.getAuthenticationManager();
 }
}

配置過濾器鏈

自定義類,處理未經(jīng)身份驗證或者身份驗證失敗的用戶訪問受保護資源時的行為。

import org.yian.springboot.demo.api.Result;
import org.yian.springboot.demo.api.ResultCode;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 處理未經(jīng)身份驗證或者身份驗證失敗的用戶訪問受保護資源時的行為
 */
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
 @Override
 public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
  String msg = StrUtil.format("請求訪問:{},認證失敗,無法訪問系統(tǒng)資源", request.getRequestURI());
  response.setStatus(200);
  response.setContentType("application/json");
  response.setCharacterEncoding("utf-8");
  response.getWriter().print(JSONUtil.toJsonStr(Result.fail(ResultCode.UNAUTHORIZED, msg)));
 }
}

配置AuthenticationManager:

import org.yian.springboot.demo.security.crypto.Sm4PasswordEncoder;
import org.yian.springboot.demo.security.filter.TokenFilter;
import org.yian.springboot.demo.service.impl.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

 @Resource
 private UserDetailsServiceImpl userDetailsService;

 @Resource
 private AuthenticationEntryPoint authenticationEntryPoint;

 @Bean
 public DaoAuthenticationProvider authenticationProvider() {
  DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
  authProvider.setUserDetailsService(userDetailsService);
  authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
 }

 @Bean
 public TokenFilter tokenFilter() {
return new TokenFilter();
 }

 @Bean
 public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
 }

 /**
  * 密碼加密方式配置
  */
 @Bean
 public PasswordEncoder passwordEncoder() {
return new Sm4PasswordEncoder();
 }

 @Bean
 public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  // 啟用跨域資源共享(CORS)支持
  http.cors()
   .and()
   // 禁用跨站請求偽造(CSRF)保護
   .csrf().disable()
   // 配置異常處理和身份驗證入口點
   .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
   .and()
   // 配置會話管理和會話創(chuàng)建策略:不使用會話
   .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
   .and()
   // 配置請求授權(quán)規(guī)則
   .authorizeRequests().antMatchers("/api/test/**").permitAll()
   .antMatchers("/api/auth/**").permitAll()
   // 所有其他請求需要進行身份驗證
   .anyRequest().authenticated();

  // 配置用戶身份驗證邏輯
  http.authenticationProvider(authenticationProvider());

  // 在UsernamePasswordAuthenticationFilter過濾器之前添加TokenFilter
  http.addFilterBefore(tokenFilter(), UsernamePasswordAuthenticationFilter.class);

return http.build();
 }
}

登錄接口

import org.yian.springboot.demo.api.Result;
import org.yian.springboot.demo.security.model.AuthUser;
import org.yian.springboot.demo.util.JwtUtil;
import lombok.AllArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@AllArgsConstructor
@RequestMapping("/api/auth")
public class LoginController {

 private final AuthenticationManager authenticationManager;

 @PostMapping("/login")
 public Result<Map<String, Object>> login(String username, String password) {
  UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
  Authentication authentication = authenticationManager.authenticate(authenticationToken);
  SecurityContextHolder.getContext().setAuthentication(authentication);
  String token = JwtUtil.createToken(username, new HashMap<>());
  AuthUser authUser = (AuthUser) authentication.getPrincipal();

  Map<String, Object> resultMap = new HashMap<>(16);
  resultMap.put("token", token);
  resultMap.put("user", authUser);

return Result.success(resultMap);
 }
}
  1. login()方法接收兩個參數(shù):username和password,表示用戶輸入的用戶名和密碼。根據(jù)用戶名、密碼創(chuàng)建一個UsernamePasswordAuthenticationToken對象;
  2. 然后調(diào)用authenticationManager.authenticate(authenticationToken)方法,使用AuthenticationManager對身份驗證令牌進行身份驗證,得到一個已經(jīng)通過身份驗證的Authentication對象;
  3. 然后調(diào)用SecurityContextHolder.getContext().setAuthentication(authentication)方法,將驗證后的Authentication對象存儲到SecurityContextHolder中,以便對用戶進行身份認證;
  4. 調(diào)用JWT工具類生成token 。調(diào)用authentication.getPrincipal()方法獲取經(jīng)過驗證的用戶信息,強制類型轉(zhuǎn)換為AuthUser類型。統(tǒng)一放在Map中返回。

測試

認證成功返回結(jié)果截圖:

圖片圖片

認證失敗返回結(jié)果截圖:

圖片圖片


責(zé)任編輯:武曉燕 來源: 一安未來
相關(guān)推薦

2021-09-17 09:00:00

安全身份認證OAuth 2.0

2024-08-27 09:28:39

2020-10-26 19:20:08

Linux國密算法加密

2009-12-23 10:46:38

WPF實現(xiàn)用戶界面

2009-12-30 09:45:52

Silverlight

2023-06-26 00:30:51

2010-01-28 10:00:54

linux用戶注銷logout

2022-09-22 10:01:47

微服務(wù)授權(quán)認證

2012-05-04 09:28:49

Linux

2011-07-07 15:32:07

2010-08-04 10:48:17

路由器

2018-04-02 10:16:00

bug代碼安卓

2016-10-24 23:18:55

數(shù)據(jù)分析漏斗留存率

2016-05-17 10:03:39

用戶體驗運維可度量

2018-05-30 10:22:47

電商平臺

2019-08-22 15:42:03

2025-03-05 07:58:30

2014-07-22 14:48:05

2024-09-22 10:46:33

數(shù)據(jù)飛輪算法
點贊
收藏

51CTO技術(shù)棧公眾號