Spring Security介紹及配置實(shí)現(xiàn)代碼
簡介
Spring Security是一個(gè)功能強(qiáng)大的Java安全框架,它提供了全面的安全認(rèn)證(Authentication)和授權(quán)(Authorization)的支持
與RBAC模型結(jié)合使用時(shí),Spring Security能夠?qū)崿F(xiàn)靈活權(quán)限控制
Spring Security配置
Spring Security的配置類是實(shí)現(xiàn)安全控制的核心部分
開啟Spring Security各種功能,以確保Web應(yīng)用程序的安全性,包括認(rèn)證、授權(quán)、會(huì)話管理、過濾器添加等
配置實(shí)現(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 */ //開啟方法級(jí)別的權(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 | 匹配所有請(qǐng)求路徑 * access | SpringEl表達(dá)式結(jié)果為true時(shí)可以訪問 * anonymous | 匿名可以訪問 * denyAll | 用戶不能訪問 * fullyAuthenticated | 用戶完全認(rèn)證可以訪問(非remember-me下自動(dòng)登錄) * hasAnyAuthority | 如果有參數(shù),參數(shù)表示權(quán)限,則其中任何一個(gè)權(quán)限可以訪問 * hasAnyRole | 如果有參數(shù),參數(shù)表示角色,則其中任何一個(gè)角色可以訪問 * 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禁用,因?yàn)椴皇褂胹ession .csrf().disable() // 禁用HTTP響應(yīng)標(biāo)頭 .headers().cacheControl().disable().and() // 認(rèn)證失敗處理類 .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() // 基于token,所以不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // 過濾請(qǐng)求 .authorizeRequests() // 對(duì)于登錄login 注冊register 驗(yàn)證碼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() // 除上面外的所有請(qǐng)求全部需要鑒權(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)散列哈希加密實(shí)現(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 { //定義一個(gè)靜態(tài)的正則表達(dá)式模式,用于匹配路徑變量 private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); //應(yīng)用上下文對(duì)象,用于獲取spirng bean private ApplicationContext applicationContext; //存儲(chǔ)允許匿名訪問的URL模式的列表 private List<String> urls = new ArrayList<>(); //通配符,用于替換路徑中的變量部分 public String ASTERISK = "*"; //在屬性設(shè)置之后執(zhí)行初始化操作 //主要用于解析請(qǐng)求映射,找出標(biāo)記為匿名訪問的方法和控制器,并將其URL模式添加至urls列表中 @Override public void afterPropertiesSet() { //從應(yīng)用上下文中獲取RequestMappingHandlerMapping的實(shí)例 RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); //獲取所有處理器方法及其對(duì)應(yīng)的請(qǐng)求映射信息 Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods(); //遍歷所有請(qǐng)求映射信息 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; /** * 處理未通過身份驗(yàn)證的請(qǐng)求 * 當(dāng)用戶嘗試訪問需要身份驗(yàn)證的資源但未通過認(rèn)證時(shí),此方法被調(diào)用 * 它向客戶端放回一個(gè)包含錯(cuò)誤信息的HTTP響應(yīng),指示認(rèn)證失敗 * @param request 請(qǐng)求對(duì)象,包含用戶嘗試訪問的URI等信息 * @param response 響應(yīng)對(duì)象,用于向客戶端發(fā)送錯(cuò)誤信息 * @param e 身份驗(yàn)證異常對(duì)象,指示用戶身份驗(yàn)證失敗的原因 * @throws IOException 如果在向客戶端發(fā)送響應(yīng)時(shí)發(fā)生輸入輸出錯(cuò)誤 * */ @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException { //HTTP狀態(tài)碼401,表示未授權(quán) int code = HttpStatus.UNAUTHORIZED; //構(gòu)建錯(cuò)誤信息,包含嘗試訪問的URI String msg = StringUtils.format("請(qǐng)求訪問:{},認(rèn)證失敗,無法訪問系統(tǒng)資源", request.getRequestURI()); //使用JSON格式向客戶端返回錯(cuò)誤信息 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(); // 刪除用戶緩存記錄,實(shí)現(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過濾器 驗(yàn)證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)證對(duì)象 if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) { //驗(yàn)證用戶令牌是否有效 tokenService.verifyToken(loginUser); //創(chuàng)建認(rèn)證對(duì)象 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); //設(shè)置認(rèn)證對(duì)象的詳細(xì)信息,這些詳細(xì)信息是基于web的認(rèn)證細(xì)節(jié) authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); //將認(rèn)證對(duì)象設(shè)置到安全上下文中,這樣應(yīng)用的其他部分可以訪問到用戶信息 SecurityContextHolder.getContext().setAuthentication(authenticationToken); } //繼續(xù)執(zhí)行過濾器鏈中的下一個(gè)過濾器或目標(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; /** * 用戶驗(yàn)證處理 * * @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")); } //驗(yàn)證用戶密碼是否正確 passwordService.validate(user); //創(chuàng)建并返回登錄用戶對(duì)象 return createLoginUser(user); } public UserDetails createLoginUser(SysUser user) { return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); } }
到此這篇關(guān)于Spring Security介紹及配置實(shí)現(xiàn)代碼的文章就介紹到這了,更多相關(guān)Spring Security介紹及配置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(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àng)目實(shí)現(xiàn)關(guān)閉數(shù)據(jù)庫配置和springSecurity
相關(guān)文章
Intellij IDEA導(dǎo)入eclipse web項(xiàng)目的操作步驟詳解
Eclipse當(dāng)中的web項(xiàng)目都會(huì)有這兩個(gè)文件,但是idea當(dāng)中應(yīng)該是沒有的,所以導(dǎo)入會(huì)出現(xiàn)兼容問題,但是本篇文章會(huì)教大家如何導(dǎo)入,并且導(dǎo)入過后還能使用tomcat運(yùn)行,需要的朋友可以參考下2023-08-08詳解SpringCloud-Alibaba-Seata分布式事務(wù)
這篇文章主要介紹了SpringCloud-Alibaba-Seata分布式事務(wù)的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12XSS攻擊以及java應(yīng)對(duì)xss攻擊的解決方案
XSS是跨站腳本攻擊Cross Site Scripting的縮寫,為了和層疊樣式表CSS加以區(qū)分,因此將跨站腳本攻擊縮寫為XSS,這篇文章主要給大家介紹了關(guān)于XSS攻擊以及java應(yīng)對(duì)xss攻擊的解決方案,需要的朋友可以參考下2024-02-02Java 創(chuàng)建兩個(gè)線程模擬對(duì)話并交替輸出實(shí)現(xiàn)解析
這篇文章主要介紹了Java 創(chuàng)建兩個(gè)線程模擬對(duì)話并交替輸出實(shí)現(xiàn)解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10淺析SpringBoot自動(dòng)化配置原理實(shí)現(xiàn)
這篇文章主要介紹了淺析SpringBoot自動(dòng)化配置原理實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06SpringBoot手動(dòng)開啟事務(wù):DataSourceTransactionManager問題
這篇文章主要介紹了SpringBoot手動(dòng)開啟事務(wù):DataSourceTransactionManager問題,具有很好的價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07