Spring security中的授權(quán)
前言
本篇為大家?guī)鞸pring security的授權(quán),首先要理解一些概念,有關(guān)于:權(quán)限、角色、安全上下文、訪問控制表達(dá)式、方法級(jí)安全性、訪問決策管理器
一.授權(quán)的基本介紹
Spring Security 中的授權(quán)分為兩種類型:
- 基于角色的授權(quán):以用戶所屬角色為基礎(chǔ)進(jìn)行授權(quán),如管理員、普通用戶等,通過為用戶分配角色來控制其對資源的訪問權(quán)限。
- 基于資源的授權(quán):以資源為基礎(chǔ)進(jìn)行授權(quán),如 URL、方法等,通過定義資源所需的權(quán)限,來控制對該資源的訪問權(quán)限。
Spring Security 提供了多種實(shí)現(xiàn)授權(quán)的機(jī)制,最常用的是使用基于注解的方式,建立起訪問資源和權(quán)限之間的映射關(guān)系。
其中最常用的兩個(gè)注解是 @Secured
和 @PreAuthorize
。@Secured
注解是更早的注解,基于角色的授權(quán)比較適用,@PreAuthorize
基于 SpEL
表達(dá)式的方式,可靈活定義所需的權(quán)限,通常用于基于資源的授權(quán)。
二.修改User配置角色和權(quán)限
方法一.
使用SQL語句的方式查詢該角色的權(quán)限,并且可以對它進(jìn)行修改
根據(jù)用戶id查詢出對應(yīng)的角色信息
SELECT * FROM sys_user a, sys_user_role b, sys_role_module c, sys_module d WHERE a.id = b.user_id and b.role_id=c.role_id and c.module_id = d.id and a.id=#{id}
根據(jù)用戶ID查詢出角色對應(yīng)的權(quán)限信息
select m.url from sys_user u,sys_user_role ur,sys_role r,sys_role_module rm,sys_module m where u.id=ur.userid and ur.roleid=r.roleid and r.roleid=rm.roleid and rm.moduleid=m.id and u.id=#{userid} and url is not null
但是并不推薦使用這種方法,當(dāng)我們在實(shí)際開發(fā)中,要考慮到不同的數(shù)據(jù)表可能來自不同的庫中,使用SQL查詢時(shí)就會(huì)出現(xiàn)鏈表查詢不同庫的表的情況,所以,更多的時(shí)候我們會(huì)使用Java利用不同的操作對表進(jìn)行依次查詢作為條件最終得到結(jié)果
方法二.利用Java對表單一查詢?nèi)缓笞鳛椴樵儣l件,最終查詢出結(jié)果
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService, UserDetailsService { @Autowired private IUserRoleService userRoleService; @Autowired private IRoleService roleService; @Autowired private IRoleModuleService roleModuleService; @Autowired private IModuleService moduleService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = getOne(new QueryWrapper<User>().eq("username", username)); if(user==null){ throw new UsernameNotFoundException("用戶名無效"); } //查詢出身份 //map遍歷所有對象,返回新的數(shù)據(jù)放到新的集合中 //filter 過濾流中的內(nèi)容 //collect將流中的元素變成一個(gè)集合 List<Integer> role_ids = userRoleService .list(new QueryWrapper<UserRole>().eq("user_id", user.getId())) .stream().map(UserRole::getRoleId) .collect(Collectors.toList()); //根據(jù)身份字段查詢身份對應(yīng)的名字字段 List<String> roles = roleService .list(new QueryWrapper<Role>()) .stream().map(Role::getRoleName) .collect(Collectors.toList()); //根據(jù)身份id查詢具備的權(quán)限id List<Integer> module_ids = roleModuleService .list(new QueryWrapper<RoleModule>().in("role_id", role_ids)) .stream().map(RoleModule::getModuleId) .collect(Collectors.toList()); //根據(jù)權(quán)限id查詢對應(yīng)的權(quán)限 List<String> modules = moduleService .list(new QueryWrapper<Module>().in("id", module_ids)) .stream().map(Module::getUrl) .collect(Collectors.toList()); // 將權(quán)限字段加到身份中 roles.addAll(modules); //將當(dāng)前集合內(nèi)容加到權(quán)限字段中 List<SimpleGrantedAuthority> authorities = roles.stream() .map(SimpleGrantedAuthority::new) .filter(Objects::nonNull) .collect(Collectors.toList()); user.setAuthorities(authorities); return user; } }
三.SpringSecurity配置類
當(dāng)我們想要開啟spring
方法級(jí)安全時(shí),只需要在任何 @Configuration
實(shí)例上使用@EnableGlobalMethodSecurity
注解就能達(dá)到此目的。同時(shí)這個(gè)注解為我們提供了prePostEnabled
、securedEnabled
和 jsr250Enabled
三種不同的機(jī)制來實(shí)現(xiàn)同一種功能。
修改WebSecurityConfig
配置類,開啟基于方法的安全認(rèn)證機(jī)制,也就是說在web層的controller啟用注解機(jī)制的安全確認(rèn)。
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig { @Autowired private UserServiceImpl userDetailsService; @Autowired private ObjectMapper objectMapper; @Autowired private MyAuthenticationFailureHandler myAuthenticationFailureHandler; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager() throws Exception { //創(chuàng)建DaoAuthenticationProvider DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); //設(shè)置userDetailsService,基于數(shù)據(jù)庫方式進(jìn)行身份認(rèn)證 provider.setUserDetailsService(userDetailsService); //配置密碼編碼器 provider.setPasswordEncoder(passwordEncoder()); return new ProviderManager(provider); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeRequests() //antMatchers 匹配對應(yīng)的路徑 //permitAll 允許 .antMatchers("/").permitAll() //anyRequest 其余所有請求 //authenticated 登錄 .anyRequest().authenticated() .and() .formLogin() //loginPage 登錄頁面 .loginPage("/") //設(shè)置處理登錄請求的接口 .loginProcessingUrl("/userLogin") //用戶的數(shù)據(jù)的參數(shù) .usernameParameter("username") .passwordParameter("password") //登錄成功 .successHandler((req, resp, auth) -> { Object user = auth.getPrincipal(); objectMapper .writeValue(resp.getOutputStream(), JsonResponseBody.success(user)); }) //登錄失敗 .failureHandler(myAuthenticationFailureHandler) .and() .exceptionHandling() //權(quán)限不足 .accessDeniedHandler((req, resp, ex) -> { objectMapper .writeValue(resp.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.NO_ACCESS)); }) //沒有認(rèn)證 .authenticationEntryPoint((req, resp, ex) -> { objectMapper .writeValue(resp.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.NO_LOGIN)); }) .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/"); http.csrf().disable(); return http.build(); } }
這里需要注意的是:
@EnableGlobalMethodSecurity
是Spring Security提供的一個(gè)注解,用于啟用方法級(jí)別的安全性。它可以在任何@Configuration類上使用,以啟用Spring Security的方法級(jí)別的安全性功能。它接受一個(gè)或多個(gè)參數(shù),用于指定要使用的安全注解類型和其他選項(xiàng)。以下是一些常用的參數(shù)
prePostEnabled
:如果設(shè)置為true
,則啟用@PreAuthorize
和@PostAuthorize
注解。默認(rèn)值為false
。
securedEnabled
:如果設(shè)置為true
,則啟用@Secured
注解。默認(rèn)值為false
。
jsr250Enabled
:如果設(shè)置為true
,則啟用@RolesAllowed
注解。默認(rèn)值為false
。
proxyTargetClass
:如果設(shè)置為true
,則使用CGLIB代理而不是標(biāo)準(zhǔn)的JDK動(dòng)態(tài)代理。默認(rèn)值為false
。使用
@EnableGlobalMethodSecurity
注解后,可以在應(yīng)用程序中使用Spring Security提供的各種注解來保護(hù)方法,例如@Secured
、@PreAuthorize
、@PostAuthorize
和@RolesAllowed
。這些注解允許您在方法級(jí)別上定義安全規(guī)則,以控制哪些用戶可以訪問哪些方法。
注解介紹:
注解 | 說明 |
---|---|
@PreAuthorize | 用于在方法執(zhí)行之前對訪問進(jìn)行權(quán)限驗(yàn)證 |
@PostAuthorize | 用于在方法執(zhí)行之后對返回結(jié)果進(jìn)行權(quán)限驗(yàn)證 |
@Secured | 用于在方法執(zhí)行之前對訪問進(jìn)行權(quán)限驗(yàn)證 |
@RolesAllowed | 是Java標(biāo)準(zhǔn)的注解之一,用于在方法執(zhí)行之前對訪問進(jìn)行權(quán)限驗(yàn)證 |
四.管理控制Controller層的權(quán)限
@Controller public class IndexController { @RequestMapping("/") public String toLogin() { return "login"; } @RequestMapping("/userLogin") public String userLogin() { return "index"; } @RequestMapping("/index") public String toIndex() { return "index"; } @RequestMapping("/noAccess") public String noAccess() { return "accessDenied"; } @ResponseBody @RequestMapping("/order_add") @PreAuthorize("hasAuthority('order:manager:add')") public String order_add() { return "訂單新增"; } @ResponseBody @PreAuthorize("hasAuthority('book:manager:add')") @RequestMapping("/book_add") public String book_add() { return "書本新增"; } }
在當(dāng)前登錄的用戶必須擁有當(dāng)前的權(quán)限字段才能進(jìn)行訪問,例如:book:manager:add
五.異常處理
AccessDeniedHandler
是Spring Security提供的一個(gè)接口,用于處理訪問被拒絕的情況。當(dāng)用戶嘗試訪問受保護(hù)資源但沒有足夠的權(quán)限時(shí),Spring Security會(huì)調(diào)用AccessDeniedHandler
來處理這種情況。
AccessDeniedHandler
接口只有一個(gè)方法handle()
,該方法接收HttpServletRequest
、HttpServletResponse
和AccessDeniedException
三個(gè)參數(shù)。在handle()
方法中,可以自定義響應(yīng)的內(nèi)容,例如返回一個(gè)自定義的錯(cuò)誤頁面或JSON
響應(yīng)。
創(chuàng)建AccessDeniedHandlerImpl
類并實(shí)現(xiàn)AccessDeniedHandler
接口,實(shí)現(xiàn)自定義的JSON
響應(yīng)。例如:
package com.yu.security.resp; import lombok.Getter; @Getter public enum JsonResponseStatus { OK(200, "OK"), UN_KNOWN(500, "未知錯(cuò)誤"), RESULT_EMPTY(1000, "查詢結(jié)果為空"), NO_ACCESS(3001, "沒有權(quán)限"), NO_LOGIN(4001, "沒有登錄"), LOGIN_FAILURE(5001, "登錄失敗"), ; private final Integer code; private final String msg; JsonResponseStatus(Integer code, String msg) { this.code = code; this.msg = msg; } }
單獨(dú)寫一個(gè)接口進(jìn)行實(shí)現(xiàn),并將出現(xiàn)異常后的操作在里面實(shí)現(xiàn)
package com.yu.security.config; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.fasterxml.jackson.databind.ObjectMapper; import com.yu.security.pojo.User; import com.yu.security.resp.JsonResponseBody; import com.yu.security.resp.JsonResponseStatus; import com.yu.security.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; @Component public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { @Autowired private ObjectMapper objectMapper; // 在redis中定義一個(gè)鍵當(dāng)?shù)卿浭∈蔷蛯δ莻€(gè)鍵的值進(jìn)行加一 //如果大于三就鎖住 @Autowired private IUserService userService; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { if(1==2){ User user = userService.getOne(new QueryWrapper<User>().eq("username", request.getParameter("username"))); user.setAccountNonLocked(false); userService.updateById(user); } objectMapper.writeValue(response.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.LOGIN_FAILURE)); } }
在當(dāng)前例子中:我們通過在配置類引入當(dāng)前接口,并實(shí)現(xiàn)當(dāng)前接口,在實(shí)現(xiàn)類中,對登錄失敗進(jìn)行 對應(yīng)的操作,在Redis中定義一個(gè)鍵當(dāng)?shù)卿浭∈蔷蛯δ莻€(gè)鍵的值進(jìn)行加一,如果大于三就對當(dāng)前賬號(hào)進(jìn)行凍結(jié)
到此這篇關(guān)于Spring security 授權(quán)的文章就介紹到這了,更多相關(guān)Spring security 授權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring gateway配置Spring Security實(shí)現(xiàn)統(tǒng)一權(quán)限驗(yàn)證與授權(quán)示例源碼
- Springboot使用Security實(shí)現(xiàn)OAuth2授權(quán)驗(yàn)證完整過程
- springboot+jwt+springSecurity微信小程序授權(quán)登錄問題
- Spring Security OAuth2 授權(quán)碼模式的實(shí)現(xiàn)
- Spring Security OAuth2認(rèn)證授權(quán)示例詳解
- Spring Security 控制授權(quán)的方法
- 詳解使用Spring Security OAuth 實(shí)現(xiàn)OAuth 2.0 授權(quán)
相關(guān)文章
Spring?web開發(fā)教程之Request獲取3種方式
這篇文章主要給大家介紹了關(guān)于Spring?web開發(fā)教程之Request獲取3種方式的相關(guān)資料,request對象是從客戶端向服務(wù)器發(fā)出請求,包括用戶提交的信息以及客戶端的一些信息,需要的朋友可以參考下2023-11-11Java8內(nèi)存模型PermGen Metaspace實(shí)例解析
這篇文章主要介紹了Java8內(nèi)存模型PermGen Metaspace實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03redis 使用lettuce 啟動(dòng)內(nèi)存泄漏錯(cuò)誤的解決方案
這篇文章主要介紹了redis 使用lettuce 啟動(dòng)內(nèi)存泄漏錯(cuò)誤的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04Java 重寫時(shí)應(yīng)當(dāng)遵守的 11 條規(guī)則
這篇文章主要介紹了Java 重寫時(shí)應(yīng)當(dāng)遵守的 11 條規(guī)則,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03Java并發(fā)包之CopyOnWriteArrayList類的深入講解
這篇文章主要給大家介紹了關(guān)于Java并發(fā)包之CopyOnWriteArrayList類的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Java從零編寫吃貨聯(lián)盟訂餐系統(tǒng)全程講解
這篇文章主要介紹了Java訂餐系統(tǒng),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-12-12