- 一、簡介
-
二、工程搭建
- 1、工程結構
- 2、依賴管理
-
三、配置管理
- 1、核心配置類
- 2、認證數(shù)據源
- 3、認證流程
- 4、身份過濾器
-
四、核心功能
- 1、登錄退出
- 2、權限校驗
- 五、參考源碼
標簽:Security.登錄.權限;
一、簡介
SpringSecurity組件可以為服務提供安全管理的能力,比如身份驗證、授權和針對常見攻擊的保護,是保護基于spring應用程序的事實上的標準;
在實際開發(fā)中,最常用的是登錄驗證和權限體系兩大功能,在登錄時完成身份的驗證,加載相關信息和角色權限,在訪問其他系統(tǒng)資源時,進行權限的驗證,保護系統(tǒng)的安全;
二、工程搭建
1、工程結構
2、依賴管理
在starter-security
依賴中,實際上是依賴spring-security
組件的6.1.1
版本,對于該框架的使用,主要是通過自定義配置類進行控制;
<!-- 安全組件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot.version}</version>
</dependency>
三、配置管理
1、核心配置類
在該類中涉及到的配置非常多,主要是服務的攔截控制,身份認證的處理流程以及過濾器等,很多自定義的處理類通過該配置進行加載;
@EnableWebSecurity
@EnableMethodSecurity
@Configuration
public class SecurityConfig {
/**
* 基礎配置
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// 配置攔截規(guī)則
httpSecurity.authorizeHttpRequests(authorizeHttpRequests->{
authorizeHttpRequests
.requestMatchers(WhiteConfig.whiteList()).permitAll()
.anyRequest().authenticated();
});
// 禁用默認的登錄和退出
httpSecurity.formLogin(AbstractHttpConfigurer::disable);
httpSecurity.logout(AbstractHttpConfigurer::disable);
httpSecurity.csrf(AbstractHttpConfigurer::disable);
// 異常時認證處理流程
httpSecurity.exceptionHandling(exeConfig -> {
exeConfig.authenticationEntryPoint(authenticationEntryPoint());
});
// 添加過濾器
httpSecurity.addFilterAt(authTokenFilter(),CsrfFilter.class);
return httpSecurity.build() ;
}
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthExeHandler();
}
@Bean
public OncePerRequestFilter authTokenFilter () {
return new AuthTokenFilter();
}
/**
* 認證管理
*/
@Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(authenticationProvider()) ;
}
/**
* 自定義用戶認證流
*/
@Bean
public AbstractUserDetailsAuthenticationProvider authenticationProvider() {
return new AuthProvider() ;
}
}
2、認證數(shù)據源
UserDetailsService
是加載用戶特定數(shù)據的核心接口,編寫用戶服務類并實現(xiàn)該接口,提供用戶信息和權限體系的數(shù)據查詢和加載,作為用戶身份識別的關鍵憑據;
@Service
public class UserService implements UserDetailsService {
@Resource
private UserBaseMapper userBaseMapper;
@Resource
private BCryptPasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
UserBase queryUser = geyByUserName(userName);
if (Objects.isNull(queryUser)){
throw new AuthException("該用戶不存在");
}
List<GrantedAuthority> grantedAuthorityList = new ArrayList<>() ;
grantedAuthorityList.add(new SimpleGrantedAuthority(queryUser.getUserRole())) ;
return new User(queryUser.getUserName(),queryUser.getPassWord(),grantedAuthorityList);
}
public int register (UserBase userBase){
if (!Objects.isNull(userBase)){
userBase.setPassWord(passwordEncoder.encode(userBase.getPassWord()));
userBase.setCreateTime(new Date()) ;
return userBaseMapper.insert(userBase) ;
}
return 0 ;
}
public UserBase getById (Integer id){
return userBaseMapper.selectById(id) ;
}
public UserBase geyByUserName (String userName){
List<UserBase> userBaseList = new LambdaQueryChainWrapper<>(userBaseMapper)
.eq(UserBase::getUserName,userName).last("limit 1").list();
if (userBaseList.size() > 0){
return userBaseList.get(0) ;
}
return null ;
}
}
3、認證流程
自定義用戶名和密碼的身份令牌認證邏輯,基于用戶名Username
從上面的用戶服務類中加載數(shù)據并校驗,在驗證成功后將用戶的身份令牌返回給調用者;
@Component
public class AuthProvider extends AbstractUserDetailsAuthenticationProvider {
private static final Logger log = LoggerFactory.getLogger(AuthProvider.class);
@Resource
private UserService userService;
@Resource
private BCryptPasswordEncoder passwordEncoder;
@Override
protected void additionalAuthenticationChecks(
UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
User user = (User) userDetails;
String loginPassword = authentication.getCredentials().toString();
log.info("user:{},loginPassword:{}",user.getPassword(),loginPassword);
if (!passwordEncoder.matches(loginPassword, user.getPassword())) {
throw new AuthException("賬號或密碼錯誤");
}
authentication.setDetails(user);
}
@Override
protected UserDetails retrieveUser(
String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
log.info("username:{}",username);
return userService.loadUserByUsername(username);
}
}
4、身份過濾器
通過繼承OncePerRequestFilter
抽象類,實現(xiàn)用戶身份的過濾器,如果不是白名單請求,需要驗證令牌是否正確有效,SecurityContextHolder
默認狀態(tài)下使用ThreadLocal
存儲信息;
@Component
public class AuthTokenFilter extends OncePerRequestFilter {
@Resource
private AuthTokenService authTokenService ;
@Resource
private AuthExeHandler authExeHandler ;
@Override
protected void doFilterInternal(@Nonnull HttpServletRequest request,
@Nonnull HttpServletResponse response,
@Nonnull FilterChain filterChain) throws ServletException, IOException {
String uri = request.getRequestURI();
if (Arrays.asList(WhiteConfig.whiteList()).contains(uri)){
// 如果是白名單直接放行
filterChain.doFilter(request,response);
} else {
String token = request.getHeader("Auth-Token");
if (Objects.isNull(token) || token.isEmpty()){
// Token不存在,攔截返回
authExeHandler.commence(request,response,null);
} else {
Object object = authTokenService.getToken(token);
if (!Objects.isNull(object) && object instanceof User user){
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user, null,user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request,response);
} else {
// Token驗證失敗,攔截返回
authExeHandler.commence(request,response,null);
}
}
}
}
}
四、核心功能
1、登錄退出
自定義登錄和退出兩個接口,基于用戶名和密碼執(zhí)行上述的身份認證流程,如果認證成功則返回用戶的身份令牌,在請求「非」白名單接口時需要在請求頭中Auth-Token:token
攜帶該令牌,在退出時會清除身份信息;
@Service
public class LoginService {
private static final Logger log = LoggerFactory.getLogger(LoginService.class);
@Resource
private AuthTokenService authTokenService ;
@Resource
private AuthenticationManager authenticationManager;
public String doLogin (UserBase userBase){
AbstractAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userBase.getUserName().trim(), userBase.getPassWord().trim());
Authentication authentication = authenticationManager.authenticate(authToken) ;
User user = (User) authentication.getDetails();
return authTokenService.createToken(user) ;
}
public Boolean doLogout (String authToken){
SecurityContextHolder.clearContext();
return authTokenService.deleteToken(authToken) ;
}
}
@Service
public class AuthTokenService {
private static final Logger log = LoggerFactory.getLogger(AuthTokenService.class);
@Resource
private RedisTemplate<String,Object> redisTemplate ;
public String createToken (User user){
String userName = user.getUsername();
String token = DigestUtils.md5DigestAsHex(userName.getBytes());
log.info("user-name:{},create-token:{}",userName,token);
redisTemplate.opsForValue().set(token,user,10, TimeUnit.MINUTES);
return token ;
}
public Object getToken (String token){
return redisTemplate.opsForValue().get(token);
}
public Boolean deleteToken (String token){
return redisTemplate.delete(token);
}
}
2、權限校驗
UserWeb
類中提供用戶的注冊接口,在用戶表中創(chuàng)建兩個測試用戶:admin
對應ROLE_Admin
角色,user
對應ROLE_User
角色,驗證如下幾個接口的權限控制;
select
接口不需要鑒權,攔截器放行即可訪問;getUser
接口校驗ROLE_User
角色;getAdmin
接口校驗ROLE_Admin
角色;query
接口校驗兩個角色中的任意一個即可;文章來源:http://www.zghlxwxcb.cn/news/detail-646433.html
兩個不同用戶登錄獲取到各自的身份令牌,使用不同的令牌請求接口,在PreAuthorize
驗證通過后才可以正常訪問;文章來源地址http://www.zghlxwxcb.cn/news/detail-646433.html
@RestController
public class UserWeb {
@Resource
private UserService userService ;
@PostMapping("/register")
public String register (@RequestBody UserBase userBase){
return "register-"+userService.register(userBase) ;
}
@GetMapping("/select/{id}")
public UserBase select (@PathVariable Integer id){
return userService.getById(id) ;
}
@PreAuthorize("hasRole('User')")
@GetMapping("/user/{id}")
public UserBase getUser (@PathVariable Integer id){
return userService.getById(id) ;
}
@PreAuthorize("hasRole('Admin')")
@GetMapping("/admin/{id}")
public UserBase getAdmin (@PathVariable Integer id){
return userService.getById(id) ;
}
@PreAuthorize("hasAnyRole('User','Admin')")
@GetMapping("/query/{id}")
public UserBase query (@PathVariable Integer id){
return userService.getById(id) ;
}
}
五、參考源碼
文檔倉庫:
https://gitee.com/cicadasmile/butte-java-note
源碼倉庫:
https://gitee.com/cicadasmile/butte-spring-parent
到了這里,關于SpringBoot3安全管理的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!