Spring Security介紹及配置實現(xiàn)代碼
簡介
Spring Security是一個功能強(qiáng)大的Java安全框架,它提供了全面的安全認(rèn)證(Authentication)和授權(quán)(Authorization)的支持
與RBAC模型結(jié)合使用時,Spring Security能夠?qū)崿F(xiàn)靈活權(quán)限控制
Spring Security配置
Spring Security的配置類是實現(xiàn)安全控制的核心部分
開啟Spring Security各種功能,以確保Web應(yīng)用程序的安全性,包括認(rèn)證、授權(quán)、會話管理、過濾器添加等
配置實現(xiàn)代碼
com.sky.framework.config.SecurityConfig
package com.sky.framework.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.web.filter.CorsFilter; import com.sky.framework.config.properties.PermitAllUrlProperties; import com.sky.framework.security.filter.JwtAuthenticationTokenFilter; import com.sky.framework.security.handle.AuthenticationEntryPointImpl; import com.sky.framework.security.handle.LogoutSuccessHandlerImpl; /** * spring security配置 * * @author ruoyi */ //開啟方法級別的權(quán)限控制 @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 自定義用戶認(rèn)證邏輯 */ @Autowired private UserDetailsService userDetailsService; /** * 認(rèn)證失敗處理類 */ @Autowired private AuthenticationEntryPointImpl unauthorizedHandler; /** * 退出處理類 */ @Autowired private LogoutSuccessHandlerImpl logoutSuccessHandler; /** * token認(rèn)證過濾器 */ @Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter; /** * 跨域過濾器 */ @Autowired private CorsFilter corsFilter; /** * 允許匿名訪問的地址 */ @Autowired private PermitAllUrlProperties permitAllUrl; /** * 解決 無法直接注入 AuthenticationManager * * @return * @throws Exception */ @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /** * anyRequest | 匹配所有請求路徑 * access | SpringEl表達(dá)式結(jié)果為true時可以訪問 * anonymous | 匿名可以訪問 * denyAll | 用戶不能訪問 * fullyAuthenticated | 用戶完全認(rèn)證可以訪問(非remember-me下自動登錄) * hasAnyAuthority | 如果有參數(shù),參數(shù)表示權(quán)限,則其中任何一個權(quán)限可以訪問 * hasAnyRole | 如果有參數(shù),參數(shù)表示角色,則其中任何一個角色可以訪問 * hasAuthority | 如果有參數(shù),參數(shù)表示權(quán)限,則其權(quán)限可以訪問 * hasIpAddress | 如果有參數(shù),參數(shù)表示IP地址,如果用戶IP和參數(shù)匹配,則可以訪問 * hasRole | 如果有參數(shù),參數(shù)表示角色,則其角色可以訪問 * permitAll | 用戶可以任意訪問 * rememberMe | 允許通過remember-me登錄的用戶訪問 * authenticated | 用戶登錄后可訪問 */ @Override protected void configure(HttpSecurity httpSecurity) throws Exception { // 注解標(biāo)記允許匿名訪問的url ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); httpSecurity // CSRF禁用,因為不使用session .csrf().disable() // 禁用HTTP響應(yīng)標(biāo)頭 .headers().cacheControl().disable().and() // 認(rèn)證失敗處理類 .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() // 基于token,所以不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // 過濾請求 .authorizeRequests() // 對于登錄login 注冊register 驗證碼captchaImage 允許匿名訪問 .antMatchers("/login", "/register", "/captchaImage").permitAll() // 靜態(tài)資源,可匿名訪問 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() // 除上面外的所有請求全部需要鑒權(quán)認(rèn)證 .anyRequest().authenticated() .and() .headers().frameOptions().disable(); // 添加Logout filter httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); // 添加JWT filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 添加CORS filter httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); } /** * 強(qiáng)散列哈希加密實現(xiàn) */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * 身份認(rèn)證接口 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); } }
com.sky.framework.config.properties.PermitAllUrlProperties
package com.sky.framework.config.properties; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.regex.Pattern; import org.apache.commons.lang3.RegExUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import com.sky.common.annotation.Anonymous; /** * 設(shè)置Anonymous注解允許匿名訪問的url * * @author ruoyi */ @Configuration public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware { //定義一個靜態(tài)的正則表達(dá)式模式,用于匹配路徑變量 private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); //應(yīng)用上下文對象,用于獲取spirng bean private ApplicationContext applicationContext; //存儲允許匿名訪問的URL模式的列表 private List<String> urls = new ArrayList<>(); //通配符,用于替換路徑中的變量部分 public String ASTERISK = "*"; //在屬性設(shè)置之后執(zhí)行初始化操作 //主要用于解析請求映射,找出標(biāo)記為匿名訪問的方法和控制器,并將其URL模式添加至urls列表中 @Override public void afterPropertiesSet() { //從應(yīng)用上下文中獲取RequestMappingHandlerMapping的實例 RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); //獲取所有處理器方法及其對應(yīng)的請求映射信息 Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods(); //遍歷所有請求映射信息 map.keySet().forEach(info -> { HandlerMethod handlerMethod = map.get(info); // 獲取方法上邊的注解 Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); // 如果方法上有Anonymous注解 將方法上的url添加至urls Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); // 獲取類上邊的注解 Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); //如果控制類上有Anonymous注解 將類下方法上的url添加至urls Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); }); } @Override public void setApplicationContext(ApplicationContext context) throws BeansException { this.applicationContext = context; } public List<String> getUrls() { return urls; } public void setUrls(List<String> urls) { this.urls = urls; } }
com.sky.framework.security.handle.AuthenticationEntryPointImpl
package com.sky.framework.security.handle; import java.io.IOException; import java.io.Serializable; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; import com.alibaba.fastjson2.JSON; import com.sky.common.constant.HttpStatus; import com.sky.common.core.domain.AjaxResult; import com.sky.common.utils.ServletUtils; import com.sky.common.utils.StringUtils; /** * 認(rèn)證失敗處理類 返回未授權(quán) * * @author ruoyi */ @Component public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; /** * 處理未通過身份驗證的請求 * 當(dāng)用戶嘗試訪問需要身份驗證的資源但未通過認(rèn)證時,此方法被調(diào)用 * 它向客戶端放回一個包含錯誤信息的HTTP響應(yīng),指示認(rèn)證失敗 * @param request 請求對象,包含用戶嘗試訪問的URI等信息 * @param response 響應(yīng)對象,用于向客戶端發(fā)送錯誤信息 * @param e 身份驗證異常對象,指示用戶身份驗證失敗的原因 * @throws IOException 如果在向客戶端發(fā)送響應(yīng)時發(fā)生輸入輸出錯誤 * */ @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException { //HTTP狀態(tài)碼401,表示未授權(quán) int code = HttpStatus.UNAUTHORIZED; //構(gòu)建錯誤信息,包含嘗試訪問的URI String msg = StringUtils.format("請求訪問:{},認(rèn)證失敗,無法訪問系統(tǒng)資源", request.getRequestURI()); //使用JSON格式向客戶端返回錯誤信息 ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); } }
com.sky.framework.security.handle.LogoutSuccessHandlerImpl
package com.sky.framework.security.handle; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import com.alibaba.fastjson2.JSON; import com.sky.common.constant.Constants; import com.sky.common.core.domain.AjaxResult; import com.sky.common.core.domain.model.LoginUser; import com.sky.common.utils.MessageUtils; import com.sky.common.utils.ServletUtils; import com.sky.common.utils.StringUtils; import com.sky.framework.manager.AsyncManager; import com.sky.framework.manager.factory.AsyncFactory; import com.sky.framework.web.service.TokenService; /** * 自定義退出處理類 返回成功 * * @author ruoyi */ @Configuration public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { @Autowired private TokenService tokenService; /** * 退出處理 * * @return */ @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { //獲取當(dāng)前登錄用戶 LoginUser loginUser = tokenService.getLoginUser(request); //檢查用戶是否已登錄 if (StringUtils.isNotNull(loginUser)) { //獲取用戶名 String userName = loginUser.getUsername(); // 刪除用戶緩存記錄,實現(xiàn)退出 tokenService.delLoginUser(loginUser.getToken()); // 記錄用戶退出日志 AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success"))); } //向客戶端返回退出成功的消息 ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success")))); } }
com.sky.framework.security.filter.JwtAuthenticationTokenFilter
package com.sky.framework.security.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import com.sky.common.core.domain.model.LoginUser; import com.sky.common.utils.SecurityUtils; import com.sky.common.utils.StringUtils; import com.sky.framework.web.service.TokenService; /** * token過濾器 驗證token有效性 * * @author ruoyi */ @Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired private TokenService tokenService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { //通過令牌服務(wù)獲取登錄用戶信息 LoginUser loginUser = tokenService.getLoginUser(request); //檢查是否已登錄且SS當(dāng)前沒有認(rèn)證對象 if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) { //驗證用戶令牌是否有效 tokenService.verifyToken(loginUser); //創(chuàng)建認(rèn)證對象 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); //設(shè)置認(rèn)證對象的詳細(xì)信息,這些詳細(xì)信息是基于web的認(rèn)證細(xì)節(jié) authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); //將認(rèn)證對象設(shè)置到安全上下文中,這樣應(yīng)用的其他部分可以訪問到用戶信息 SecurityContextHolder.getContext().setAuthentication(authenticationToken); } //繼續(xù)執(zhí)行過濾器鏈中的下一個過濾器或目標(biāo)servlet chain.doFilter(request, response); } }
com.sky.framework.web.service.UserDetailsServiceImpl#loadUserByUsername
package com.sky.framework.web.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; 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 com.sky.common.core.domain.entity.SysUser; import com.sky.common.core.domain.model.LoginUser; import com.sky.common.enums.UserStatus; import com.sky.common.exception.ServiceException; import com.sky.common.utils.MessageUtils; import com.sky.common.utils.StringUtils; import com.sky.system.service.ISysUserService; /** * 用戶驗證處理 * * @author ruoyi */ @Service public class UserDetailsServiceImpl implements UserDetailsService { private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); @Autowired private ISysUserService userService; @Autowired private SysPasswordService passwordService; @Autowired private SysPermissionService permissionService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //通過用戶名查詢用戶信息 SysUser user = userService.selectUserByUserName(username); //檢查用戶是否存在 if (StringUtils.isNull(user)) { log.info("登錄用戶:{} 不存在.", username); //拋出異常提示用戶不存在 throw new ServiceException(MessageUtils.message("user.not.exists")); } //檢查用戶是否已經(jīng)被刪除 else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { log.info("登錄用戶:{} 已被刪除.", username); //拋出異常提示用戶已經(jīng)被刪除 throw new ServiceException(MessageUtils.message("user.password.delete")); } //檢查用戶是否被停用 else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登錄用戶:{} 已被停用.", username); //拋出異常提示用戶已經(jīng)被停用 throw new ServiceException(MessageUtils.message("user.blocked")); } //驗證用戶密碼是否正確 passwordService.validate(user); //創(chuàng)建并返回登錄用戶對象 return createLoginUser(user); } public UserDetails createLoginUser(SysUser user) { return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); } }
到此這篇關(guān)于Spring Security介紹及配置實現(xiàn)代碼的文章就介紹到這了,更多相關(guān)Spring Security介紹及配置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring Security 中的 AuthenticationManager配置及使用
- 新版SpringSecurity5.x使用與配置詳解
- SpringSecurity6.x多種登錄方式配置小結(jié)
- Spring Security6配置方法(廢棄WebSecurityConfigurerAdapter)
- SpringSecurity表單配置之登錄成功及頁面跳轉(zhuǎn)原理解析
- SpringBoot2.7?WebSecurityConfigurerAdapter類過期配置
- 解決Spring Security的權(quán)限配置不生效問題
- SpringBoot項目實現(xiàn)關(guān)閉數(shù)據(jù)庫配置和springSecurity
相關(guān)文章
Intellij IDEA導(dǎo)入eclipse web項目的操作步驟詳解
Eclipse當(dāng)中的web項目都會有這兩個文件,但是idea當(dāng)中應(yīng)該是沒有的,所以導(dǎo)入會出現(xiàn)兼容問題,但是本篇文章會教大家如何導(dǎo)入,并且導(dǎo)入過后還能使用tomcat運(yùn)行,需要的朋友可以參考下2023-08-08詳解SpringCloud-Alibaba-Seata分布式事務(wù)
這篇文章主要介紹了SpringCloud-Alibaba-Seata分布式事務(wù)的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12Java 創(chuàng)建兩個線程模擬對話并交替輸出實現(xiàn)解析
這篇文章主要介紹了Java 創(chuàng)建兩個線程模擬對話并交替輸出實現(xiàn)解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-10-10SpringBoot手動開啟事務(wù):DataSourceTransactionManager問題
這篇文章主要介紹了SpringBoot手動開啟事務(wù):DataSourceTransactionManager問題,具有很好的價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07