当前位置: 首页 > news >正文

【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, "订单列表");}

相关文章:

【SpringSecurity】认证与鉴权框架SpringSecurity——授权

目录 权限系统的必要性常见的权限管理框架SpringSecurity授权基本流程准备脚本限制访问资源所需权限菜单实体类和Mapper封装权限信息封装认证/鉴权失败处理认证失败封装鉴权失败封装配置SpringSecurity 过滤器跨域处理接口添加鉴权hasAuthority/hasAnyAuthorityhasRole/​ hasA…...

深入解析FTP:原理、架构与搭建方式

在当今互联网世界中&#xff0c;文件传输是日常工作和生活中不可或缺的一部分。FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;作为一种老而弥坚的协议&#xff0c;一直在文件传输领域发挥着重要作用。本文将从技术人的角度&#xff0c;详细分析F…...

Springboot与RestTemplate

RestTemplate是Spring提供的用于访问Rest服务的客户端&#xff0c;RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。 一、使用Get进行访问 1、获取json格式 使用 getForEntity() API 发起 GET 请求&#xff1a; RestTemplate restTemplate…...

端口发布与暴露

端口发布与暴露 目录 发布端口发布到临时端口发布所有端口试一试 使用 Docker CLI使用 Docker Compose 如果你一直在跟随本指南&#xff0c;你应该理解容器为应用程序的每个组件提供了隔离的进程。每个组件 - 如 React 前端、Python API 和 Postgres 数据库 - 都运行在自己的…...

Unity:使用Texture2D动态创建的图像无法正常显示 / 修改图像后未生效

开发中遇到需要动态绘制图像的需求&#xff0c;前后文代码如下所示&#xff1a; Texture2D newImageTexture new Texture2D(width, height); Color32[] newImagePixels new Color32[height * width];for (int y 0; y < height ; y) {for (int x 0; x < width; x){if…...

【LinuxC语言】详解TCP/IP

文章目录 前言TCP与UDP协议的介绍TCP协议流式传输TCP的三次握手连接TCP的四次挥手连接断开总结前言 在我们的日常生活中,无论是浏览网页,还是发送电子邮件,甚至是在线视频聊天,都离不开网络通信。而在网络通信中,TCP和UDP协议起着至关重要的作用。本文将以通俗易懂的语言…...

数字化转型下的企业人力资源信息系统研究

随着数字化转型的加速&#xff0c;企业人力资源管理面临着全新的挑战和机遇。传统的人力资源信息系统&#xff08;HRIS&#xff09;在新时代的要求下必须进行深刻的革新和升级&#xff0c;以更好地支持企业的发展战略和员工的需求。 数据驱动的决策支持 在当今这个信息化迅猛发…...

docker camunda 8.5 部署步骤

Camunda Platform 8 环境准备 Docker 版本要求 Docker 20.10.16 is required; docker compose version 1.27.0.;github 开源地址:https://github.com/camunda/camunda-platformcamunda7 文档地址:https://docs.camunda.org/manual/7.21/user-guide/process-engine/社区地址: …...

学懂C#编程:常用高级技术——委托(Delegate)应用场景——委托与Lambda表达式的结合使用详解

在C#中&#xff0c;委托与Lambda表达式的结合使用是现代编程实践中的一个重要且强大的特性&#xff0c;它极大地提高了代码的简洁性和可读性。下面将详细讲解这两个概念如何协同工作&#xff0c;以及如何在实际编程中有效利用它们。 委托基础 委托是C#中的一种引用类型&#x…...

05-java基础——循环习题

循环的选择&#xff1a;知道循环的次数或者知道循环的范围就使用for循环&#xff0c;其次再使用while循环 猜数字 程序自动生成一个1-100之间的随机数&#xff0c;在代码中使用键盘录入去猜出这个数字是多少&#xff1f; 要求&#xff1a;使用循环猜&#xff0c;一直猜中为止…...

网络安全等级保护测评

网络安全等级保护 《GB17859 计算机信息系统安全保护等级划分准则》 规定计算机信息系统安全保护等级共分五级 《中华人民共和国网络安全法》 “国家实行网络安全等级保护制度。 等级测评 测评机构依据国家网络安全等级保护制度规定&#xff0c;按照有关 管理规范和…...

真有被这套零售数据分析方案惊艳到

在数字化时代&#xff0c;零售行业的竞争愈发激烈&#xff0c;如何精准把握市场动态、优化业务流程、提升市场竞争力成为了每一家零售企业都面临的挑战。奥威BI零售数据分析方案&#xff0c;一款具备全面、高效、智能的数据分析能力的BI方案出现地恰到好处。 奥威BI零售数据分…...

亚马逊卖家为何需要自养账号?揭秘背后的原因

亚马逊是一家极为重视用户体验的国际电商平台&#xff0c;因此用户的评论和星级评价对店铺排名影响深远。平台审核评论非常严格&#xff0c;这些评价直接影响商品在平台上的生存和发展。 在亚马逊上&#xff0c;用户的评分和评论对商品的成功至关重要。好评多的商品通常被认为优…...

牛了,LSTM+Transformer王炸结合创新,荣登Nature,精度高达95.65%

【LSTM结合Transformer】的研究方向探索了如何利用Transformer模型处理序列数据的能力以及LSTM在捕捉时间序列依赖性方面的优势。这一方向的意义在于通过融合两种模型的特点&#xff0c;提高了对复杂时空数据的预测准确性&#xff0c;尤其是在智能电网攻击检测、多变量时间序列…...

Java面试题:通过实例说明工厂模式和抽象工厂模式的用法,以及它们在解耦中的作用

工厂模式和抽象工厂模式是创建型设计模式中的两种&#xff0c;主要用于对象的创建&#xff0c;并且通过将对象的创建过程封装起来&#xff0c;来实现代码的解耦和灵活性。下面通过具体实例来说明这两种模式的用法及其在解耦中的作用。 工厂模式&#xff08;Factory Method Pat…...

成都欣丰洪泰文化传媒有限公司电商服务的创新者

在数字化浪潮席卷全球的今天&#xff0c;电商行业正以前所未有的速度蓬勃发展。作为这一领域的佼佼者&#xff0c;成都欣丰洪泰文化传媒有限公司凭借其对电商服务的深度理解和精准把握&#xff0c;成功在竞争激烈的市场中脱颖而出&#xff0c;成为行业内的佼佼者。 一、公司简…...

学习笔记——动态路由——RIP(距离矢量协议)

一、距离矢量协议 1、距离矢量协议 矢量行为&#xff1a;协议收到一个路由之后&#xff0c;查看是否可以加入到本地的路由表中&#xff0c;如果可以加入&#xff0c;则可以传递&#xff0c;如果不可以加入&#xff0c;则无法传递。 距离矢量路由协议 RIP基于距离矢量算法(又…...

【python】OpenCV—Segmentation

文章目录 cv2.kmeans牛刀小试 cv2.kmeans cv2.kmeans 是 OpenCV 库中用于执行 K-Means 聚类算法的函数。以下是根据参考文章整理的 cv2.kmeans 函数的中文文档&#xff1a; 一、函数功能 cv2.kmeans 用于执行 K-Means 聚类算法&#xff0c;将一组数据点划分到 K 个簇中&…...

python-题库篇-Python语言特性

文章目录 Python语言特性1 Python的函数参数传递2 Python中的元类(metaclass)3 staticmethod和classmethod4 类变量和实例变量5 Python自省6 字典推导式7 Python中单下划线和双下划线8 字符串格式化:%和.format9 迭代器和生成器10 *args and **kwargs11 面向切面编程AOP和装饰器…...

WEB界面上使用ChatGPT

&#xff08;作者&#xff1a;陈玓玏&#xff09; 开源项目&#xff0c;欢迎star哦&#xff0c;https://github.com/tencentmusic/cube-studio 随着大模型不断发展&#xff0c;现在无论写代码&#xff0c;做设计&#xff0c;甚至老师备课、评卷都可以通过AI大模型来实现了&…...

【Matlab】CNN-LSTM分类 卷积神经网络-长短期记忆神经网络组合模型(附代码)

资源下载&#xff1a; https://download.csdn.net/download/vvoennvv/89466499 分类算法资源合集&#xff1a;https://download.csdn.net/download/vvoennvv/89466519 目录 Matlab SVM支持向量机分类算法 Matlab RF随机森林分类算法 Matlab RBF径向基神经网络分类算法 Ma…...

性能工具之 MySQL OLTP Sysbench BenchMark 测试示例

文章目录 一、前言二、测试环境1、服务器配置2、测试拓扑 三、测试工具安装四、测试步骤1、导入数据2、压测数据3、清理数据 五、结果解析六、最后 一、前言 做为一名性能工程师掌握对 MySQL 的性能测试是非常必要的&#xff0c;本文基于 Sysbench 对MySQL OLTP&#xff08;联…...

【QT】QCustomPlot库中iSelectPlottables的使用

QCP::iSelectPlottables 是 QCustomPlot 库中的一个枚举值&#xff0c;用于控制选择交互。QCustomPlot 是一个用于创建绘图和数据可视化的Qt库。 QCP::iSelectPlottables 允许用户选择图表中的绘图对象&#xff08;如图形、曲线、柱状图等&#xff09;。 应用场景 QCP::iSele…...

字节跳动联手博通:5nm AI芯片诞生了?

字节跳动联手博通&#xff1a;5nm AI芯片诞生了&#xff1f; 前言 就在6月24日&#xff0c;字节跳动正在与美国博通合作开发一款5纳米工艺的专用集成电路(ASIC) AI处理器。这款芯片旨在降低采购成本并确保高端AI芯片的稳定供应。 根据报道&#xff0c;尽管芯片设计工作进展顺利…...

【数据结构与算法】动态查找表(二叉排序树,二叉平衡树)详解

二叉排序树的数据结构。 struct TreeNode {ElemType data;TreeNode *left, *right; }; using BiTree TreeNode *;结构体包含三个成员&#xff1a; data 是一个 ElemType 类型的变量&#xff0c;用于存储二叉搜索树节点的数据。left 是一个指向 TreeNode 类型的指针&#xff…...

PyTorch中“No module named ‘torch._six‘“的报错场景及处理方法

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 在使用PyTorch时&#xff0c;您可能会遇到"No module named ‘torch._six’"的错误。这通常是因为PyTorch的某些…...

Spring Boot 集成 MinIO 实现文件上传

Spring Boot 集成 MinIO 实现文件上传 一、 Minio 服务准备 MinIO的搭建过程参考 Docker 搭建 MinIO 对象存储。 登录MinIO控制台&#xff0c;新建一个 Bucket&#xff0c;修改 Bucket 权限为公开。 二、MinIO 集成 添加 MinIO 依赖 <!-- https://mvnrepository.com/ar…...

目标跟踪——KCF源码用python实现

from numpy.fft import fft2, ifft2, fftshift import cv2 import numpy as npclass HOG:def __init__(self, winSize):""":param winSize: 检测窗口的大小"""self.winSize winSizeself.blockSize (8, 8)self.blockStride (4, 4)self.cellSiz…...

前端 转换笔记

<!DOCTYPE html> <html> <head> <meta charset"utf-8" /> <title>转换</title> <style> .box{ /* 盒子摆在body的正中间 */ position: absolut…...

个人开发笔记

开发笔记 开发常见问题Vue开发中页面flex滚动布局&#xff0c;内容置顶问题功能快捷键 开发常见问题 Vue开发中页面flex滚动布局&#xff0c;内容置顶问题 直接操作路由&#xff1a; const router createRouter({routes: routes,history: createWebHashHistory(),scrollBeha…...