這里寫自定義目錄標題
- 引言
- SpringSecurity之授權
- 授權介紹
- java權限集成
- 登錄失敗三次用戶上鎖

引言
SpringSecurity深度解析與實踐(2)的網址
SpringSecurity之授權
授權介紹
Spring Security 中的授權分為兩種類型:
- 基于角色的授權:以用戶所屬角色為基礎進行授權,如管理員、普通用戶等,通過為用戶分配角色來控制其對資源的訪問權限。
- 基于資源的授權:以資源為基礎進行授權,如 URL、方法等,通過定義資源所需的權限,來控制對該資源的訪問權限。
Spring Security 提供了多種實現授權的機制,最常用的是使用基于注解的方式,建立起訪問資源和權限之間的映射關系。
其中最常用的兩個注解是 @Secured
和 @PreAuthorize
。@Secured
注解是更早的注解,基于角色的授權比較適用,@PreAuthorize
基于 SpEL
表達式的方式,可靈活定義所需的權限,通常用于基于資源的授權。
java權限集成
WebSecurityConfig配置類
package com.yuan.springsecurity1.config;import com.fasterxml.jackson.databind.ObjectMapper;
import com.yuan.springsecurity1.resp.JsonResponseBody;
import com.yuan.springsecurity1.resp.JsonResponseStatus;
import com.yuan.springsecurity1.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;import javax.sql.DataSource;@Configuration
//開啟SpringSecurity的默認行為
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Autowiredprivate DataSource dataSource;/*** 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置*/@Beanpublic PersistentTokenRepository persistentTokenRepository(){JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);// 設置為true要保障數據庫該表不存在,不然會報異常哦// 所以第二次打開服務器應用程序的時候得把它設為falsetokenRepository.setCreateTableOnStartup(false);return tokenRepository;}@Autowiredprivate MyUserDetailsService myUserDetailsService;@AutowiredObjectMapper objectMapper;@AutowiredMyAuthenticationFailureHandler myAuthenticationFailureHandler;/*** 獲取AuthenticationManager(認證管理器),登錄時認證使用(基于數據庫方式)* @param* @return* @throws Exception*/@Beanpublic AuthenticationManager authenticationManager() throws Exception {//創建DaoAuthenticationProviderDaoAuthenticationProvider provider=new DaoAuthenticationProvider();//設置userDetailsService,基于數據庫方式進行身份認證provider.setUserDetailsService(myUserDetailsService);//配置密碼編碼器provider.setPasswordEncoder(passwordEncoder());return new ProviderManager(provider);}@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http)throws Exception{http.authorizeRequests()// 開放接口訪問權限,不需要登錄就可以訪問.antMatchers("/toLogin").permitAll()//訪問路徑有admin的方法時,需要有ADMIN的身份
// .antMatchers("/admin/**").hasRole("ADMIN")
// .antMatchers("/user/**").hasAnyRole("ADMIN","USER")// 其余所有請求全部需要鑒權認證.anyRequest().authenticated().and().formLogin()// 設置登錄頁面的 URL.loginPage("/toLogin")// 設置登錄請求的 URL,即表單提交的 URL.loginProcessingUrl("/userLogin")// 設置登錄表單中用戶名字段的參數名,默認為username.usernameParameter("username")// 設置登錄表單中密碼字段的參數名,默認為password.passwordParameter("password")//成功后的處理.successHandler((req,resp,auth)->{
// resp.sendRedirect("/index");Object user = auth.getPrincipal();objectMapper.writeValue(resp.getOutputStream(), JsonResponseBody.success(user));})//登錄失敗的處理.failureHandler(myAuthenticationFailureHandler).and().exceptionHandling()
// .accessDeniedPage("/noAccess")//權限不夠處理.accessDeniedHandler((req,resp,ex)->{objectMapper.writeValue(resp.getOutputStream(),JsonResponseBody.other(JsonResponseStatus.NO_ACCESS));})//沒有認證(登錄)處理.authenticationEntryPoint((req,resp,ex)->{objectMapper.writeValue(resp.getOutputStream(),JsonResponseBody.other(JsonResponseStatus.NO_LOGIN));}).and().logout()// 設置安全退出的URL路徑.logoutUrl("/logout")// 設置退出成功后跳轉的路徑.logoutSuccessUrl("/toLogin").and().rememberMe()// 指定 rememberMe 的參數名,用于在表單中攜帶 rememberMe 的值。.rememberMeParameter("remember-me")// 指定 rememberMe 的有效期,單位為秒,默認2周。.tokenValiditySeconds(600)// 指定 rememberMe 的 cookie 名稱。.rememberMeCookieName("remember-me-cookie")// 指定 rememberMe 的 token 存儲方式,可以使用默認的 PersistentTokenRepository 或自定義的實現。.tokenRepository(persistentTokenRepository())// 指定 rememberMe 的認證方式,需要實現 UserDetailsService 接口,并在其中查詢用戶信息。.userDetailsService(myUserDetailsService);
// http.csrf().disable();return http.build();}
}
登錄權限綁定
package com.yuan.springsecurity1.config;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.yuan.springsecurity1.pojo.*;
import com.yuan.springsecurity1.mapper.UserMapper;
import com.yuan.springsecurity1.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
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.Component;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;/*** <p>* 用戶信息表 服務實現類* </p>** @author yuan* @since 2023-12-21*/
@Component
public class MyUserDetailsService implements UserDetailsService {@Autowiredprivate IUserService iUserService;@Autowiredprivate IUserRoleService iUserRoleService;@Autowiredprivate IRoleService iRoleService;@Autowiredprivate IRoleModuleService iRoleModuleService;@Autowiredprivate IModuleService iModuleService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = iUserService.getOne(new QueryWrapper<User>().eq("username", username));if(user==null)throw new UsernameNotFoundException("用戶名無效");//查詢所有的身份,map 遍歷數據對象,返回的數據放到一個新的流中,collect(Collectors.toList())用list集合接收List<Integer> userIds = iUserRoleService.list(new QueryWrapper<UserRole>().eq("user_id", user.getId())).stream().map(UserRole::getRoleId).collect(Collectors.toList());//根據role_id拿到身份名稱集合List<String> roleNames = iRoleService.list(new QueryWrapper<Role>().in("role_id", userIds)).stream().map(Role::getRoleName).collect(Collectors.toList());//根據身份id role_id拿到權限id module_idList<Integer> moduleIds = iRoleModuleService.list(new QueryWrapper<RoleModule>().in("role_id", userIds)).stream().map(RoleModule::getModuleId).collect(Collectors.toList());//根據權限id拿到對應的urlList<String> urls = iModuleService.list(new QueryWrapper<Module>().in("id", moduleIds)).stream().map(Module::getUrl).filter(Objects::nonNull).collect(Collectors.toList());roleNames.addAll(urls);List<SimpleGrantedAuthority> authorities = roleNames.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());user.setAuthorities(authorities);return user;}
}
Controller類數據
@ResponseBody@RequestMapping("/o_f")@PreAuthorize("hasAnyAuthority('order:manager:analysis')")public String o_f() {return "訂單分析";}@ResponseBody@RequestMapping("/b_add")@PreAuthorize("hasAnyAuthority('book:manager:add')")public String b_add() {return "書籍新增";}
需要添加一個規則,判斷是否有權限,我這個放在WebSecurityConfig類上
@EnableGlobalMethodSecurity(prePostEnabled = true)
登錄失敗三次用戶上鎖
搭配WebSecurityConfig類中失敗的對象
package com.yuan.springsecurity1.config;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yuan.springsecurity1.pojo.User;
import com.yuan.springsecurity1.resp.JsonResponseBody;
import com.yuan.springsecurity1.resp.JsonResponseStatus;
import com.yuan.springsecurity1.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @author 葉秋* @site* @company 卓京公司* @create 2023-12-23 23:21*/
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {@AutowiredObjectMapper objectMapper;@Autowiredprivate IUserService iUserService;@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {if(1==2){ //假設超過三次失敗User user = iUserService.getOne(new QueryWrapper<User>().eq("username", request.getParameter("username")));user.setAccountNonLocked(false);iUserService.updateById(user);}objectMapper.writeValue(response.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.LOGIN_FAILURE));}
}