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

一.授權(quán)的基本介紹
Spring Security 中的授權(quán)分為兩種類型:
- 基于角色的授權(quán):以用戶所屬角色為基礎(chǔ)進(jìn)行授權(quán),如管理員、普通用戶等,通過(guò)為用戶分配角色來(lái)控制其對(duì)資源的訪問(wèn)權(quán)限。
- 基于資源的授權(quán):以資源為基礎(chǔ)進(jìn)行授權(quán),如 URL、方法等,通過(guò)定義資源所需的權(quán)限,來(lái)控制對(duì)該資源的訪問(wèn)權(quán)限。
Spring Security 提供了多種實(shí)現(xiàn)授權(quán)的機(jī)制,最常用的是使用基于注解的方式,建立起訪問(wèn)資源和權(quán)限之間的映射關(guān)系。
其中最常用的兩個(gè)注解是 @Secured 和 @PreAuthorize。@Secured 注解是更早的注解,基于角色的授權(quán)比較適用,@PreAuthorize 基于 SpEL 表達(dá)式的方式,可靈活定義所需的權(quán)限,通常用于基于資源的授權(quán)。
二.修改User配置角色和權(quán)限
方法一.
使用SQL語(yǔ)句的方式查詢?cè)摻巧臋?quán)限,并且可以對(duì)它進(jìn)行修改
根據(jù)用戶id查詢出對(duì)應(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查詢出角色對(duì)應(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)我們?cè)趯?shí)際開(kāi)發(fā)中,要考慮到不同的數(shù)據(jù)表可能來(lái)自不同的庫(kù)中,使用SQL查詢時(shí)就會(huì)出現(xiàn)鏈表查詢不同庫(kù)的表的情況,所以,更多的時(shí)候我們會(huì)使用Java利用不同的操作對(duì)表進(jìn)行依次查詢作為條件最終得到結(jié)果
方法二.利用Java對(duì)表單一查詢?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("用戶名無(wú)效");
}
//查詢出身份
//map遍歷所有對(duì)象,返回新的數(shù)據(jù)放到新的集合中
//filter 過(guò)濾流中的內(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ù)身份字段查詢身份對(duì)應(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查詢對(duì)應(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)我們想要開(kāi)啟spring方法級(jí)安全時(shí),只需要在任何 @Configuration實(shí)例上使用@EnableGlobalMethodSecurity 注解就能達(dá)到此目的。同時(shí)這個(gè)注解為我們提供了prePostEnabled 、securedEnabled 和 jsr250Enabled 三種不同的機(jī)制來(lái)實(shí)現(xiàn)同一種功能。
修改WebSecurityConfig配置類,開(kāi)啟基于方法的安全認(rèn)證機(jī)制,也就是說(shuō)在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ù)庫(kù)方式進(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 匹配對(duì)應(yīng)的路徑
//permitAll 允許
.antMatchers("/").permitAll()
//anyRequest 其余所有請(qǐng)求
//authenticated 登錄
.anyRequest().authenticated()
.and()
.formLogin()
//loginPage 登錄頁(yè)面
.loginPage("/")
//設(shè)置處理登錄請(qǐng)求的接口
.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));
})
//沒(méi)有認(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提供的各種注解來(lái)保護(hù)方法,例如@Secured、@PreAuthorize、@PostAuthorize和@RolesAllowed。這些注解允許您在方法級(jí)別上定義安全規(guī)則,以控制哪些用戶可以訪問(wèn)哪些方法。
注解介紹:
| 注解 | 說(shuō)明 |
|---|---|
@PreAuthorize | 用于在方法執(zhí)行之前對(duì)訪問(wèn)進(jìn)行權(quán)限驗(yàn)證 |
@PostAuthorize | 用于在方法執(zhí)行之后對(duì)返回結(jié)果進(jìn)行權(quán)限驗(yàn)證 |
@Secured | 用于在方法執(zhí)行之前對(duì)訪問(wèn)進(jìn)行權(quán)限驗(yàn)證 |
@RolesAllowed | 是Java標(biāo)準(zhǔn)的注解之一,用于在方法執(zhí)行之前對(duì)訪問(wèn)進(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 "書(shū)本新增";
}
}在當(dāng)前登錄的用戶必須擁有當(dāng)前的權(quán)限字段才能進(jìn)行訪問(wèn),例如:book:manager:add
五.異常處理
AccessDeniedHandler是Spring Security提供的一個(gè)接口,用于處理訪問(wèn)被拒絕的情況。當(dāng)用戶嘗試訪問(wèn)受保護(hù)資源但沒(méi)有足夠的權(quán)限時(shí),Spring Security會(huì)調(diào)用AccessDeniedHandler來(lái)處理這種情況。
AccessDeniedHandler接口只有一個(gè)方法handle(),該方法接收HttpServletRequest、HttpServletResponse和AccessDeniedException三個(gè)參數(shù)。在handle()方法中,可以自定義響應(yīng)的內(nèi)容,例如返回一個(gè)自定義的錯(cuò)誤頁(yè)面或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, "沒(méi)有權(quán)限"),
NO_LOGIN(4001, "沒(méi)有登錄"),
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ù)卿浭∈蔷蛯?duì)那個(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)前例子中:我們通過(guò)在配置類引入當(dāng)前接口,并實(shí)現(xiàn)當(dāng)前接口,在實(shí)現(xiàn)類中,對(duì)登錄失敗進(jìn)行 對(duì)應(yīng)的操作,在Redis中定義一個(gè)鍵當(dāng)?shù)卿浭∈蔷蛯?duì)那個(gè)鍵的值進(jìn)行加一,如果大于三就對(duì)當(dāng)前賬號(hào)進(jìn)行凍結(jié)
到此這篇關(guān)于Spring security 授權(quán)的文章就介紹到這了,更多相關(guān)Spring security 授權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(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)證完整過(guò)程
- springboot+jwt+springSecurity微信小程序授權(quán)登錄問(wè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開(kāi)發(fā)教程之Request獲取3種方式
這篇文章主要給大家介紹了關(guān)于Spring?web開(kāi)發(fā)教程之Request獲取3種方式的相關(guān)資料,request對(duì)象是從客戶端向服務(wù)器發(fā)出請(qǐng)求,包括用戶提交的信息以及客戶端的一些信息,需要的朋友可以參考下2023-11-11
Java8內(nèi)存模型PermGen Metaspace實(shí)例解析
這篇文章主要介紹了Java8內(nèi)存模型PermGen Metaspace實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
redis 使用lettuce 啟動(dòng)內(nèi)存泄漏錯(cuò)誤的解決方案
這篇文章主要介紹了redis 使用lettuce 啟動(dòng)內(nèi)存泄漏錯(cuò)誤的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04
Java 重寫時(shí)應(yīng)當(dāng)遵守的 11 條規(guī)則
這篇文章主要介紹了Java 重寫時(shí)應(yīng)當(dāng)遵守的 11 條規(guī)則,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
基于Java解析國(guó)密數(shù)字證書(shū)的操作方法
在Java環(huán)境中解析使用國(guó)密算法(如SM3WITHSM2)的數(shù)字證書(shū)可能遇到挑戰(zhàn),使用BouncyCastle加密庫(kù)可以解決Java標(biāo)準(zhǔn)庫(kù)無(wú)法識(shí)別國(guó)密算法橢圓曲線的問(wèn)題,成功解析國(guó)密數(shù)字證書(shū),添加BouncyCastle依賴并修改代碼,使其支持國(guó)密算法,即可解析采用SM3WITHSM2算法的數(shù)字證書(shū)2024-09-09
Java并發(fā)包之CopyOnWriteArrayList類的深入講解
這篇文章主要給大家介紹了關(guān)于Java并發(fā)包之CopyOnWriteArrayList類的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Java從零編寫吃貨聯(lián)盟訂餐系統(tǒng)全程講解
這篇文章主要介紹了Java訂餐系統(tǒng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-12-12

