【SpringSecurity】認證與鑒權框架SpringSecurity——授權

目錄

  • 權限系統的必要性
  • 常見的權限管理框架
  • SpringSecurity授權
    • 基本流程
    • 準備腳本
    • 限制訪問資源所需權限
    • 菜單實體類和Mapper
    • 封裝權限信息
    • 封裝認證/鑒權失敗處理
      • 認證失敗封裝
      • 鑒權失敗封裝
      • 配置SpringSecurity
    • 過濾器
    • 跨域處理
    • 接口添加鑒權
      • hasAuthority/hasAnyAuthority
      • hasRole/? hasAnyRole
    • 自定義權限校驗方法

權限系統的必要性

權限系統在現代軟件開發、信息管理系統、網絡服務和各種數字平臺中扮演著至關重要的角色,其必要性主要體現在以下幾個方面:

  • 安全控制:權限系統是保護數據和資源安全的第一道防線。通過限制對敏感信息和關鍵功能的訪問,可以有效防止未經授權的訪問、修改或泄露,從而降低安全風險。

  • 職責分離:在組織內部,不同的用戶或角色擁有不同的職責。權限系統確保每個用戶只能訪問和操作與他們的工作職責相關的系統部分,這有助于實現職責分離和內部控制,減少錯誤和欺詐的可能性。

  • 合規性要求:許多行業都有嚴格的數據保護法規和標準(如GDPR、HIPAA等),要求對個人信息和敏感數據進行嚴格的訪問控制。權限系統幫助組織符合這些法律法規的要求,避免法律風險和罰款。

  • 提升用戶體驗:通過為不同用戶提供定制化的界面和功能,權限系統可以減少信息過載,使用戶更容易找到他們需要的信息和服務,從而提升整體的用戶體驗。

  • 審計追蹤:權限系統能夠記錄用戶的訪問和操作日志,這對于事后審計、故障排查和安全事件調查至關重要。這不僅有助于及時發現并解決問題,也為追究責任提供了依據。

  • 靈活性和可擴展性:隨著組織的發展和需求的變化,權限系統允許管理員靈活地調整權限設置,新增或刪除用戶角色,以及對系統功能進行細粒度的控制,保證了系統的長期穩定性和可擴展性。

總之,權限系統是維護信息安全、支持組織管理和滿足法律法規要求的基礎架構,對于保障數字環境的穩定、安全和高效運行具有不可替代的作用。

常見的權限管理框架

Java Web開發中,為了實現權限管理,開發者常采用一些成熟的權限框架來簡化開發流程和提高系統安全性。以下是一些常見的Java權限管理框架:

  • Spring Security:這是Java領域中最受歡迎和廣泛使用的安全框架之一,它為Web應用程序提供了一整套安全解決方案,包括認證(Authentication)和授權(Authorization)。Spring Security支持多種認證機制(如JWT、OAuth2)、自定義權限控制,并且能夠無縫集成到Spring Boot應用中。

  • Apache Shiro:Shiro是一個強大且易用的安全框架,它提供身份驗證、授權、會話管理以及加密等功能。相比Spring Security,Shiro的學習曲線更平緩,適用于需要快速實現安全功能的項目。Shiro支持多種環境,不僅限于Web應用,也適用于命令行應用、Swing應用等。

  • JBoss Keycloak:Keycloak是一個開源的Identity and Access Management (IAM)系統,提供了單一登錄(SSO)、身份管理、社交登錄等特性。它可以通過OpenID Connect、OAuth 2.0等協議與Java Web應用集成,非常適合構建大型分布式系統的權限管理。

  • Spring Authorization Server:這是Spring生態系統中用于構建授權服務器的新項目,特別適合需要實現OAuth2協議的場景。雖然它本身不直接處理應用程序的權限控制邏輯,但與Spring Security結合使用,可以構建出強大的認證和授權體系。

  • Apache Ranger:雖然更多被用于大數據平臺(如Hadoop、Hive、Kafka)的權限管理,但Apache Ranger也可以應用于其他Java Web項目中,特別是那些需要復雜數據權限控制的場景。

選擇合適的權限框架時,需要根據項目的具體需求、團隊熟悉度、系統規模以及是否需要支持特定的安全協議等因素綜合考慮。

SpringSecurity授權

基本流程

  • SpringSecurity是使用默認的FilterSecurityInterceptor來進行權限校驗。
  • 在FilterSecurityInterceptor中會從SecurityContextHolder獲取其中的Authentication,然后獲取其中的權限信息。
  • 并以此來判斷當前用戶是否擁有訪問當前資源所需的權限。
  • 因此需要把用戶成功登錄后的的權限信息也存入Authentication, 然后設置資源所需要的權限即可。

準備腳本

/*Navicat Premium Data TransferSource Server         : 本機連接Source Server Type    : MySQLSource Server Version : 50744Source Host           : localhost:3306Source Schema         : kgc_powerTarget Server Type    : MySQLTarget Server Version : 50744File Encoding         : 65001Date: 24/06/2024 15:07:37
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account`  (`id` int(11) NOT NULL,`accountCode` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`accountName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`accountPassword` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES (1, 'admin', '系統管理員', '$2a$10$dJ1Ct/QuOOJTkhNkYeFh8uCeEqpVUO.ZjiPOfPsmbsWlX3ZIp.kDa');
INSERT INTO `account` VALUES (2, 'zhangsan', '張三', '$2a$10$dJ1Ct/QuOOJTkhNkYeFh8uCeEqpVUO.ZjiPOfPsmbsWlX3ZIp.kDa');
INSERT INTO `account` VALUES (3, 'lisi', '李四', '$2a$10$dJ1Ct/QuOOJTkhNkYeFh8uCeEqpVUO.ZjiPOfPsmbsWlX3ZIp.kDa');
INSERT INTO `account` VALUES (4, 'wangwu', '王五', '$2a$10$dJ1Ct/QuOOJTkhNkYeFh8uCeEqpVUO.ZjiPOfPsmbsWlX3ZIp.kDa');
INSERT INTO `account` VALUES (5, 'zhaoliu', '趙六', '$2a$10$dJ1Ct/QuOOJTkhNkYeFh8uCeEqpVUO.ZjiPOfPsmbsWlX3ZIp.kDa');-- ----------------------------
-- Table structure for account_role
-- ----------------------------
DROP TABLE IF EXISTS `account_role`;
CREATE TABLE `account_role`  (`accountId` bigint(200) NOT NULL AUTO_INCREMENT COMMENT '用戶id',`roleId` bigint(200) NOT NULL DEFAULT 0 COMMENT '角色id',PRIMARY KEY (`accountId`, `roleId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of account_role
-- ----------------------------
INSERT INTO `account_role` VALUES (1, 1);
INSERT INTO `account_role` VALUES (2, 2);
INSERT INTO `account_role` VALUES (3, 2);
INSERT INTO `account_role` VALUES (4, 3);
INSERT INTO `account_role` VALUES (5, 3);-- ----------------------------
-- Table structure for menu
-- ----------------------------
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu`  (`id` bigint(20) NOT NULL AUTO_INCREMENT,`menuName` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'NULL' COMMENT '菜單名',`path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路由地址',`component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '組件路徑',`visible` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '菜單狀態(0顯示 1隱藏)',`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '菜單狀態(0正常 1停用)',`perms` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '權限標識',`icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '#' COMMENT '菜單圖標',`createBy` bigint(20) NULL DEFAULT NULL,`createTime` datetime NULL DEFAULT NULL,`updateBy` bigint(20) NULL DEFAULT NULL,`updateTime` datetime NULL DEFAULT NULL,`delFlag` int(11) NULL DEFAULT 0 COMMENT '是否刪除(0未刪除 1已刪除)',`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '備注',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '菜單表' ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of menu
-- ----------------------------
INSERT INTO `menu` VALUES (1, '訂單管理', '/order', '/order', '0', '0', 'system:order', '#', NULL, NULL, NULL, NULL, 0, NULL);
INSERT INTO `menu` VALUES (2, '系統管理', '/sys', '/sys', '0', '0', 'system:sys', '#', NULL, NULL, NULL, NULL, 0, NULL);
INSERT INTO `menu` VALUES (3, '個人中心', '/info', '/info', '0', '0', 'system:info', '#', NULL, NULL, NULL, NULL, 0, NULL);
INSERT INTO `menu` VALUES (4, '商品管理', '/product', '/product', '0', '0', 'system:product', '#', NULL, NULL, NULL, NULL, 0, NULL);-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`roleKey` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色權限字符串',`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '角色狀態(0正常 1停用)',`delFlag` int(1) NULL DEFAULT 0 COMMENT 'del_flag',`createBy` bigint(200) NULL DEFAULT NULL,`createTime` datetime NULL DEFAULT NULL,`updateBy` bigint(200) NULL DEFAULT NULL,`updateTime` datetime NULL DEFAULT NULL,`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '備注',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色表' ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '系統管理員', 'ADMIN', '0', 0, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `role` VALUES (2, '供應商', 'PROVIDER', '0', 0, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `role` VALUES (3, '需求方', 'CONSUMER', '0', 0, NULL, NULL, NULL, NULL, NULL);-- ----------------------------
-- Table structure for role_menu
-- ----------------------------
DROP TABLE IF EXISTS `role_menu`;
CREATE TABLE `role_menu`  (`roleId` bigint(200) NOT NULL AUTO_INCREMENT COMMENT '角色ID',`menuId` bigint(200) NOT NULL DEFAULT 0 COMMENT '菜單id',PRIMARY KEY (`roleId`, `menuId`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of role_menu
-- ----------------------------
INSERT INTO `role_menu` VALUES (1, 2);
INSERT INTO `role_menu` VALUES (1, 3);
INSERT INTO `role_menu` VALUES (2, 1);
INSERT INTO `role_menu` VALUES (2, 3);
INSERT INTO `role_menu` VALUES (3, 1);
INSERT INTO `role_menu` VALUES (3, 3);
INSERT INTO `role_menu` VALUES (3, 4);SET FOREIGN_KEY_CHECKS = 1;

限制訪問資源所需權限

  • SpringSecurity提供了基于注解的權限控制方案,可以使用注解去指定訪問對應的資源所需的權限, 但是要使用它需要先開啟相關配置。
  • 啟動類上加@EnableGlobalMethodSecurity(prePostEnabled = true)

菜單實體類和Mapper

package com.micro.pojo;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;/*** @author: zjl* @datetime: 2024/6/24* @desc: 復興Java,我輩義不容辭*/
@Data
public class Menu implements Serializable {private Long id;/*** 菜單名*/private String menuName;/*** 路由地址*/private String path;/*** 組件路徑*/private String component;/*** 菜單狀態(0顯示 1隱藏)*/private String visible;/*** 菜單狀態(0正常 1停用)*/private String status;/*** 權限標識*/private String perms;/*** 菜單圖標*/private String icon;private Long createBy;private Date createTime;private Long updateBy;private Date updateTime;/*** 是否刪除(0未刪除 1已刪除)*/private Integer delFlag;/*** 備注*/private String remark;
}
List<String> selectPermsByAccountId(Integer accountId);
    <select id="selectPermsByAccountId" resultType="string" parameterType="int">SELECTDISTINCT M.`PERMS`FROMACCOUNT_ROLE ARLEFT JOIN `ROLE` R ON AR.`ROLEID` = R.`ID`LEFT JOIN `ROLE_MENU` RM ON AR.`ROLEID` = RM.`ROLEID`LEFT JOIN `MENU` M ON M.`ID` = RM.`MENUID`WHEREACCOUNTID = #{accountId}AND R.`STATUS` = 0AND M.`STATUS` = 0</select>
mybatis:mapper-locations: classpath:mapper/*.xmlconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

封裝權限信息

在UserDetailsServiceImpl中去調用該mapper的方法查詢權限信息封裝到LoginUser對象中

@Service
public class AccountDetailsServiceImpl implements UserDetailsService {@Resourceprivate AccountMapper accountMapper;@Resourceprivate MenuMapper menuMapper;@Overridepublic UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {Account account = accountMapper.selectAccountByAccountCode(userName);if(Objects.isNull(account)){throw new RuntimeException("用戶名或密碼錯誤");}//根據用戶查詢權限信息 LoginAccountList<String> permissionKeyList =  menuMapper.selectPermsByAccountId(account.getId());//封裝成UserDetails對象返回return new LoginAccount(account, permissionKeyList);}
}
package com.micro.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;/*** @author: zjl* @datetime: 2024/6/22* @desc: 復興Java,我輩義不容辭*/
@Data
@NoArgsConstructor
public class LoginAccount implements UserDetails {private Account account;private List<String> permissions;@JSONField(serialize = false)private List<GrantedAuthority> authorities;public LoginAccount(Account account,List<String> permissions) {this.account = account;this.permissions = permissions;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if(authorities!=null){return authorities;}//把permissions中字符串類型的權限信息轉換成GrantedAuthority對象存入authorities中authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());return authorities;}@Overridepublic String getPassword() {return account.getAccountPassword();}@Overridepublic String getUsername() {return account.getAccountName();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

封裝認證/鑒權失敗處理

  • 在SpringSecurity中,如果在認證或者授權的過程中出現了異常會被ExceptionTranslationFilter捕獲到。在ExceptionTranslationFilter中會去判斷是認證失敗還是授權失敗出現的異常。
    • 認證過程中出現的異常可以被封裝成AuthenticationException,然后調用AuthenticationEntryPoint 對象的方法去進行異常處理。
    • 如果是授權過程中出現的異常可以被封裝成AccessDeniedException,然后調用AccessDeniedHandler對象的方法去進行異常處理。

認證失敗封裝

package com.micro.service;import com.alibaba.fastjson.JSON;
import com.micro.utils.ResponseResult;
import com.micro.utils.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @author: zjl* @datetime: 2024/6/24* @desc: 復興Java,我輩義不容辭*/@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {ResponseResult result = new ResponseResult(HttpStatus.UNAUTHORIZED.value(), "認證失敗請重新登錄");String json = JSON.toJSONString(result);WebUtils.renderString(response,json);}
}

鑒權失敗封裝

package com.micro.service;import com.alibaba.fastjson.JSON;
import com.micro.utils.ResponseResult;
import com.micro.utils.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @author: zjl* @datetime: 2024/6/24* @desc: 復興Java,我輩義不容辭*/
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {ResponseResult result = new ResponseResult(HttpStatus.FORBIDDEN.value(), "權限不足");String json = JSON.toJSONString(result);WebUtils.renderString(response,json);}
}

配置SpringSecurity

package com.micro.config;import com.micro.filter.JwtAuthenticationTokenFilter;
import com.micro.service.AccessDeniedHandlerImpl;
import com.micro.service.AuthenticationEntryPointImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import javax.annotation.Resource;/*** @author: zjl* @datetime: 2024/6/22* @desc: 復興Java,我輩義不容辭*/@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Resourceprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;@Resourceprivate AccessDeniedHandlerImpl accessDeniedHandler;@Resourceprivate AuthenticationEntryPointImpl authenticationEntryPoint;@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http//關閉csrf.csrf().disable()//不通過Session獲取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 對于登錄接口 允許匿名訪問.antMatchers("/login").anonymous()// 除上面外的所有請求全部需要鑒權認證.anyRequest().authenticated();//把token校驗過濾器添加到過濾器鏈中http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler);}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}

過濾器

package com.micro.filter;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.micro.pojo.Account;
import com.micro.pojo.LoginAccount;
import com.micro.utils.JwtUtil;
import com.micro.utils.RedisStringUtil;
import io.jsonwebtoken.Claims;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Objects;/*** @author: zjl* @datetime: 2024/6/22* @desc: 復興Java,我輩義不容辭*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Resourceprivate RedisStringUtil redisStringUtil;@Resourceprivate ObjectMapper objectMapper;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//獲取tokenString token = request.getHeader("token");if (!StringUtils.hasText(token)) {//放行filterChain.doFilter(request, response);return;}//解析tokenString userid;try {Claims claims = JwtUtil.parseJWT(token);userid = claims.getSubject();} catch (Exception e) {e.printStackTrace();throw new RuntimeException("token非法");}//從redis中獲取用戶信息String redisKey = "login:" + userid;LoginAccount loginAccount = JSON.parseObject(redisStringUtil.get(redisKey), LoginAccount.class);if(Objects.isNull(loginAccount)){throw new RuntimeException("用戶未登錄");}//存入SecurityContextHolder//獲取權限信息封裝到Authentication中UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(loginAccount,null,loginAccount.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authenticationToken);//放行filterChain.doFilter(request, response);}
}

跨域處理

package com.micro.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @author: zjl* @datetime: 2024/6/24* @desc: 復興Java,我輩義不容辭*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {// 設置允許跨域的路徑registry.addMapping("/**")// 設置允許跨域請求的域名.allowedOrigins("*")// 是否允許cookie.allowCredentials(true)// 設置允許的請求方式.allowedMethods("GET", "POST", "DELETE", "PUT")// 設置允許的header屬性.allowedHeaders("*")// 跨域允許時間.maxAge(60 * 60);}
}

SecurityConfig類中configure()方法添加以下代碼

//允許跨域http.cors();

接口添加鑒權

SpringSecurity提供了以下方法:hasAuthority,hasAnyAuthority,hasRole,hasAnyRole等。

hasAuthority/hasAnyAuthority

  • hasAuthority方法內部調用authentication的getAuthorities方法獲取用戶的權限列表。然后判斷存入的方法參數數據在權限列表中。
  • ? hasAnyAuthority方法可以傳入多個權限,只有用戶有其中任意一個權限都可以訪問對應資源。
@RestController
@RequestMapping("/order")
public class OrderController {@GetMapping("/list")//@PreAuthorize("hasAnyAuthority('system:aaa','system:bbb','system:order')")@PreAuthorize("hasAuthority('system:order')")public ResponseResult list(){return new ResponseResult(200, "訂單列表");}
}

在這里插入圖片描述在這里插入圖片描述

hasRole/? hasAnyRole

  • hasRole要求有對應的角色才可以訪問,但是它內部會把傳入的參數拼接上 ROLE_ 后再去比較。所以這種情況下要用用戶對應的權限也要有 ROLE_ 這個前綴才可以。
  • hasAnyRole 有任意的角色就可以訪問。它內部也會把傳入的參數拼接上 ROLE_ 后再去比較。所以這種情況下要用用戶對應的權限也要有 ROLE_ 這個前綴才可以。

修改數據庫
在這里插入圖片描述

    @GetMapping("/list")//@PreAuthorize("hasAnyAuthority('system:aaa','system:bbb','system:order')")//@PreAuthorize("hasAuthority('system:order')")@PreAuthorize("hasRole('system:order')")public ResponseResult list(){return new ResponseResult(200, "訂單列表");}

自定義權限校驗方法

@Component("ex")
public class KgcExpressionRoot {public boolean hasAuthority(String authority){//獲取當前用戶的權限Authentication authentication = SecurityContextHolder.getContext().getAuthentication();LoginAccount loginAccount = (LoginAccount) authentication.getPrincipal();List<String> permissions = loginAccount.getPermissions();//判斷用戶權限集合中是否存在authorityreturn permissions.contains(authority);}
}
  • ? 在SPEL表達式中使用 @ex相當于獲取容器中bean的名字未ex的對象。然后再調用這個對象的hasAuthority方法
    @GetMapping("/list")//@PreAuthorize("hasAnyAuthority('system:aaa','system:bbb','system:order')")//@PreAuthorize("hasAuthority('system:order')")//@PreAuthorize("hasRole('system:order')")@PreAuthorize("@ex.hasAuthority('system:order')")public ResponseResult list(){return new ResponseResult(200, "訂單列表");}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/34997.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/34997.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/34997.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

華為HCIP Datacom H12-821 卷10

1.多選題 以下哪些動態路由協議可以應用在 IPv6 網絡? A、Is- Is B、BGP6 C、IS-ISv6 D、OSPFv3 正確答案: A,D 解析: 幾乎每個動態路由協議都支持IPv6,但是每個協議支持IPv6的時候的叫法不相同。支持IPv6的RIP協議,叫做RIPng;支持IPv6的OSPF協議,叫做OSPFv3;支持…

針對知識圖譜使用 Mistral-7b 從簡歷中提取實體

翻譯&#xff1a;“Entity Extraction from Resume using Mistral-7b for Knowledge Graphs” | by Tejpal Kumawat | Feb, 2024 | Medium[1] 在快速發展的自然語言處理&#xff08;NLP&#xff09;領域&#xff0c;從非結構化文本源中準確提取和分析信息的能力變得越來越重要。…

Python教程:認識一下print函數

print() 是 Python 中一個非常基礎但功能強大的函數&#xff0c;用于將數據輸出到標準輸出&#xff08;通常是控制臺&#xff09;或文件。本文我們一起聊一下這個“平凡”的print函數。 原理 print() 函數的原理相對簡單&#xff0c;它接受一個或多個參數&#xff0c;并將這些…

ravynOS 0.5.0 發布 - 基于 FreeBSD 的 macOS 兼容開源操作系統

ravynOS 0.5.0 發布 - 基于 FreeBSD 的 macOS 兼容開源操作系統 ravynOS - 一個旨在提供 macOS 的精致性和 FreeBSD 的自由度的操作系統 請訪問原文鏈接&#xff1a;https://sysin.org/blog/ravynos/&#xff0c;查看最新版。原創作品&#xff0c;轉載請保留出處。 作者主頁…

snakeyaml從1.x升級2.x的方案

一、背景 因公司漏洞掃描&#xff0c;發現SnakeYAML 反序列化漏洞(CVE-2022-1471)&#xff0c;所以要求對SnakYaml進行升級。 因項目中未直接引用snakyaml包&#xff0c;經分析是springboot引用的這個包。但是在這個項目中&#xff0c;springboot用的版本是2.3.12.RELEASE版本…

睡眠剝奪對記憶鞏固的神經生物學影響

近期&#xff0c;《自然》雜志刊載的研究揭示了睡眠不足對記憶相關神經信號的不利影響&#xff0c;強調了即使在后續恢復充分睡眠的情況下&#xff0c;這種損害亦難以完全逆轉。 神經元作為大腦的基本功能單位&#xff0c;其活動并非孤立進行&#xff0c;而是通過復雜的網絡連接…

QT拖放事件之四:自定義拖放操作-利用QDrag來拖動完成數據的傳輸-案例demo

1、核心代碼 #include "Widget.h" #include "ui_Widget.h" #include "MyButton.h"Widget::Widget(QWidget *parent): QWidget

CSS3 分頁

CSS3 分頁 分頁是網頁設計中常見的一種布局方式&#xff0c;它允許將內容分布在多個頁面中&#xff0c;從而提高用戶體驗和網站的可管理性。CSS3 提供了多種靈活的方式來設計分頁&#xff0c;使得開發者能夠創建既美觀又實用的分頁導航。本文將詳細介紹如何使用 CSS3 來創建和…

python 正則表達式提取字符串

以某個字符開始、某個字符結束&#xff0c;期待的提取結果包含首末字符串 提取公式&#xff1a;a re.findall(“開始字符串.*末字符串”,str) 以某個字符開始、某個字符結束&#xff0c;期待的提取結果不包含末字符串&#xff0c;但包含首字符串 提取公式&#xff1a;a re.…

Cesium--旋轉3dtiles

以下代碼來自Cesium 論壇&#xff1a;3DTileset rotation - CesiumJS - Cesium Community 在1.118中測試可行&#xff0c;可直接在Sandcastle中運行&#xff1a; const viewer new Cesium.Viewer("cesiumContainer", {terrain: Cesium.Terrain.fromWorldTerrain()…

機器學習課程復習——線性回歸

Q&#xff1a;回歸和分類的區別&#xff1f; 回歸是連續的&#xff0c;分類是離散的 Q:用最小二乘法對線性回歸模型進行參數估計思路 例題

排序。。。

1. 掌握常用的排序方法&#xff0c;并掌握用高級語言實現排序算法的方法&#xff1b; 2. 深刻理解排序的定義和各種排序方法的特點&#xff0c;并能加以靈活應用&#xff1b; 3. 了解各種方法的排序過程及其時間復雜度的分析方法。 編程實現如下功能&#xff1a; &#xff08;1…

Makefile中error函數的用法

在 Makefile 中&#xff0c;error 函數是一個特殊的函數&#xff0c;用于在執行過程中生成一個錯誤消息并終止 Makefile 的執行。它的基本語法如下&#xff1a; $(error error-message)其中&#xff0c;error-message 是一個字符串&#xff0c;表示要顯示的錯誤消息。當 Makef…

vue+three.js渲染3D模型

安裝three.js: npm install three 頁面部分代碼&#xff1a; <div style"width: 100%; height: 300px; position: relative;"><div style"height: 200px; background-color: white; width: 100%; position: absolute; top: 0;"><div id&…

【繞過無限Debugger】

文章目錄 引言無限Debugger的工作原理繞過無限Debugger的常用技巧條件斷點法置空法代碼修改與加密 引言 在Web開發中&#xff0c;debugger語句是一種強大的JavaScript功能&#xff0c;允許開發者在代碼中設置斷點&#xff0c;便于調試和理解代碼執行流程。然而&#xff0c;這一…

【文末附gpt升級秘笈】程序的“通用性”與“過度設計”的困境

程序的“通用性”與“過度設計”的困境 四、解決方案的深入闡述 &#xff08;一&#xff09;明確需求和目標&#xff1a;需求驅動設計 在軟件開發的初期&#xff0c;我們需要與業務團隊緊密合作&#xff0c;深入了解項目的實際需求和目標。這不僅包括明確的功能需求&#xf…

filelist中+incdir+的用法

在大多數 Verilog 編譯器&#xff08;如 VCS、ModelSim/Questa、Verilator&#xff09;中&#xff0c;使用 incdir 選項指定包含路徑后&#xff0c;仍然需要在 filelist 文件中列出每一個 Verilog 源文件。incdir 選項僅告訴編譯器在特定目錄中查找頭文件&#xff08;例如 .vh …

go語言day4 引入第三方依賴 整型和字符串轉換 進制間轉換 指針類型 浮點數類型 字符串類型

Golang依賴下載安裝失敗解決方法_安裝go依賴超時怎么解決-CSDN博客 go安裝依賴包&#xff08;go get, go module&#xff09;_go 安裝依賴-CSDN博客 目錄 go語言項目中如何使用第三方依賴&#xff1a;&#xff08;前兩步可以忽略&#xff09; 一、安裝git&#xff0c;安裝程序…

linux學習week1

linux學習 一.介紹 1.概述 linux的讀法不下10種 linux是一個開源的操作系統&#xff0c;操作系統包括mac、windows、安卓等 linux的開發版&#xff1a;Ubuntu&#xff08;烏班圖&#xff09;、RedHat&#xff08;紅帽&#xff09;、CentOS linux的應用&#xff1a;linux在服…