Spring security中的授權(quán)
前言
本篇為大家?guī)鞸pring security的授權(quán),首先要理解一些概念,有關(guān)于:權(quán)限、角色、安全上下文、訪問控制表達式、方法級安全性、訪問決策管理器

一.授權(quán)的基本介紹
Spring Security 中的授權(quán)分為兩種類型:
- 基于角色的授權(quán):以用戶所屬角色為基礎(chǔ)進行授權(quán),如管理員、普通用戶等,通過為用戶分配角色來控制其對資源的訪問權(quán)限。
- 基于資源的授權(quán):以資源為基礎(chǔ)進行授權(quán),如 URL、方法等,通過定義資源所需的權(quán)限,來控制對該資源的訪問權(quán)限。
Spring Security 提供了多種實現(xiàn)授權(quán)的機制,最常用的是使用基于注解的方式,建立起訪問資源和權(quán)限之間的映射關(guān)系。
其中最常用的兩個注解是 @Secured 和 @PreAuthorize。@Secured 注解是更早的注解,基于角色的授權(quán)比較適用,@PreAuthorize 基于 SpEL 表達式的方式,可靈活定義所需的權(quán)限,通常用于基于資源的授權(quán)。
二.修改User配置角色和權(quán)限
方法一.
使用SQL語句的方式查詢該角色的權(quá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)我們在實際開發(fā)中,要考慮到不同的數(shù)據(jù)表可能來自不同的庫中,使用SQL查詢時就會出現(xiàn)鏈表查詢不同庫的表的情況,所以,更多的時候我們會使用Java利用不同的操作對表進行依次查詢作為條件最終得到結(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將流中的元素變成一個集合
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方法級安全時,只需要在任何 @Configuration實例上使用@EnableGlobalMethodSecurity 注解就能達到此目的。同時這個注解為我們提供了prePostEnabled 、securedEnabled 和 jsr250Enabled 三種不同的機制來實現(xiàn)同一種功能。
修改WebSecurityConfig配置類,開啟基于方法的安全認證機制,也就是說在web層的controller啟用注解機制的安全確認。
@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ù)庫方式進行身份認證
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));
})
//沒有認證
.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提供的一個注解,用于啟用方法級別的安全性。它可以在任何@Configuration類上使用,以啟用Spring Security的方法級別的安全性功能。它接受一個或多個參數(shù),用于指定要使用的安全注解類型和其他選項。以下是一些常用的參數(shù)
prePostEnabled:如果設(shè)置為true,則啟用@PreAuthorize和@PostAuthorize注解。默認值為false。
securedEnabled:如果設(shè)置為true,則啟用@Secured注解。默認值為false。
jsr250Enabled:如果設(shè)置為true,則啟用@RolesAllowed注解。默認值為false。
proxyTargetClass:如果設(shè)置為true,則使用CGLIB代理而不是標(biāo)準(zhǔn)的JDK動態(tài)代理。默認值為false。使用
@EnableGlobalMethodSecurity注解后,可以在應(yīng)用程序中使用Spring Security提供的各種注解來保護方法,例如@Secured、@PreAuthorize、@PostAuthorize和@RolesAllowed。這些注解允許您在方法級別上定義安全規(guī)則,以控制哪些用戶可以訪問哪些方法。
注解介紹:
| 注解 | 說明 |
|---|---|
@PreAuthorize | 用于在方法執(zhí)行之前對訪問進行權(quán)限驗證 |
@PostAuthorize | 用于在方法執(zhí)行之后對返回結(jié)果進行權(quán)限驗證 |
@Secured | 用于在方法執(zhí)行之前對訪問進行權(quán)限驗證 |
@RolesAllowed | 是Java標(biāo)準(zhǔn)的注解之一,用于在方法執(zhí)行之前對訪問進行權(quá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)限字段才能進行訪問,例如:book:manager:add
五.異常處理
AccessDeniedHandler是Spring Security提供的一個接口,用于處理訪問被拒絕的情況。當(dāng)用戶嘗試訪問受保護資源但沒有足夠的權(quán)限時,Spring Security會調(diào)用AccessDeniedHandler來處理這種情況。
AccessDeniedHandler接口只有一個方法handle(),該方法接收HttpServletRequest、HttpServletResponse和AccessDeniedException三個參數(shù)。在handle()方法中,可以自定義響應(yīng)的內(nèi)容,例如返回一個自定義的錯誤頁面或JSON響應(yīng)。
創(chuàng)建AccessDeniedHandlerImpl類并實現(xiàn)AccessDeniedHandler接口,實現(xiàn)自定義的JSON響應(yīng)。例如:
package com.yu.security.resp;
import lombok.Getter;
@Getter
public enum JsonResponseStatus {
OK(200, "OK"),
UN_KNOWN(500, "未知錯誤"),
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;
}
}單獨寫一個接口進行實現(xiàn),并將出現(xiàn)異常后的操作在里面實現(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中定義一個鍵當(dāng)?shù)卿浭∈蔷蛯δ莻€鍵的值進行加一
//如果大于三就鎖住
@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)前接口,并實現(xiàn)當(dāng)前接口,在實現(xiàn)類中,對登錄失敗進行 對應(yīng)的操作,在Redis中定義一個鍵當(dāng)?shù)卿浭∈蔷蛯δ莻€鍵的值進行加一,如果大于三就對當(dāng)前賬號進行凍結(jié)
到此這篇關(guān)于Spring security 授權(quán)的文章就介紹到這了,更多相關(guān)Spring security 授權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring gateway配置Spring Security實現(xiàn)統(tǒng)一權(quán)限驗證與授權(quán)示例源碼
- Springboot使用Security實現(xiàn)OAuth2授權(quán)驗證完整過程
- springboot+jwt+springSecurity微信小程序授權(quán)登錄問題
- Spring Security OAuth2 授權(quán)碼模式的實現(xiàn)
- Spring Security OAuth2認證授權(quán)示例詳解
- Spring Security 控制授權(quán)的方法
- 詳解使用Spring Security OAuth 實現(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-11
Java8內(nèi)存模型PermGen Metaspace實例解析
這篇文章主要介紹了Java8內(nèi)存模型PermGen Metaspace實例解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03
redis 使用lettuce 啟動內(nèi)存泄漏錯誤的解決方案
這篇文章主要介紹了redis 使用lettuce 啟動內(nèi)存泄漏錯誤的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04
Java 重寫時應(yīng)當(dāng)遵守的 11 條規(guī)則
這篇文章主要介紹了Java 重寫時應(yīng)當(dāng)遵守的 11 條規(guī)則,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
Java并發(fā)包之CopyOnWriteArrayList類的深入講解
這篇文章主要給大家介紹了關(guān)于Java并發(fā)包之CopyOnWriteArrayList類的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Java從零編寫吃貨聯(lián)盟訂餐系統(tǒng)全程講解
這篇文章主要介紹了Java訂餐系統(tǒng),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-12-12

