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

SpringBoot项目实现用户token和资源的多重校验

1、需求

在spring security双token机制实现一文中已经实现了token的校验,在实际的项目中还需要根据用户的角色或用户Id对数据资源进行校验。
例如,有两个项目A和B,张三和李四都是项目实施人员这一角色,张三是项目A的项目组成员,李四是项目B的项目组成员,他们只能访问自己所属项目的资源。

2、实现

这里对spring security双token机制实现中的代码进行一部分改造。

2.1 改造TokenAuthenticationFilter

部分公共资源是可以不需要项目的权限就可以访问的,在前面的例子中,比如一些公共的模块(公司组织架构查询,帮助等)。token校验成功后可以将我们请求的资源需要的校验写入上下文中,以便过滤器链处理(代码中的NOTE注释)。

@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private UserMapper userMapper;

    private TokenMapper tokenMapper;

    private static final Logger logger = LoggerFactory.getLogger(TokenAuthenticationFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (userMapper == null) {
            userMapper = SpringUtils.getBean(UserMapper.class);
        }
        if (tokenMapper == null) {
            tokenMapper = SpringUtils.getBean(TokenMapper.class);
        }
        String tokenHeaderStr = request.getHeader("authorization");
        String token = tokenHeaderStr.substring(7);
        String userId = authenticateToken(token);
        if (userId == null || userId.isEmpty()) {
            logger.error("无效的token");
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.setCharacterEncoding("UTF-8");
            response.getWriter().write("无效的token");
            return;
        }
        User user = userMapper.findByUserIdWithRole(userId);
        String requestUri = request.getServletPath();

        // NOTE: 在上面校验用户的逻辑完成之后可以通过这样的方式将资源权限相关的属性写入Authentication上下文中,这里getNeedProjectAuth()方法获取该接口是否需要校验用户的项目权限
        List<GrantedAuthority> authorityList =
                AuthorityUtils.createAuthorityList(user.getRole().getRoleCode(), getNeedProjectAuth(requestUri).toString());

		// NOTE: 这里将用户的信息写入Authentication上下文
        Authentication authentication =
                new UsernamePasswordAuthenticationToken(user, token, authorityList);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        filterChain.doFilter(request, response);
    }

	// 实现shouldNotFilter方法,设置无需token校验的url
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String fullUri = request.getRequestURI();
        String context_path = request.getContextPath();
        String uri = fullUri.substring(fullUri.indexOf(context_path) + context_path.length());
        return Arrays.asList(acceptUrls).contains(uri);
    }
    
    private String authenticateToken(String tokenStr) {
        Token token = tokenMapper.getTokenByAccessToken(tokenStr);
        if (token == null) {
            return null;
        }
        LocalDateTime now = LocalDateTime.now();
        if (token.getAccessExpireTime().isAfter(now)) {
            return token.getUserId();
        }
        return null;
    }
}

2.2 实现ProjectAuthenticationFilter

校验token成功之后通过ProjectAuthenticationFilter 来校验用户的项目权限,从上下文中读取Authentication,获得项目权限校验信息(根据url判断是否需要校验项目权限,用户是否具有该项目的权限)

@Component
public class ProjectAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private ProjectUserMapper projectUserMapper;

    private static final Logger logger = LoggerFactory.getLogger(ProjectAuthenticationFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        // ADMIN用户可以访问所有的项目资源
        if (auth.getAuthorities().toArray()[0].toString().toUpperCase().equals("ROLE_ADMIN")) {
            filterChain.doFilter(request, response);
        } else {
    		/**
     		* 获取用户是否有请求项目资源的权限,方法略
     		*/

            filterChain.doFilter(request, response);
        }
    }

	// 在这里判断是否需要校验权限
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
    	// NOTE:从上下文中获取Authentication ,根据接口所需资源情况判断是否需要校验用户的项目权限
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        boolean needProjectAuth = Boolean.parseBoolean(auth.getAuthorities().toArray()[1].toString());
        return !needProjectAuth;
    }
}

2.3 按校验顺序注册过滤器

这里需要改造下spring security配置类,按照恰当的顺序注册过滤器链(代码中的NOTE注释)。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private TokenAuthenticationFilter tokenAuthenticationFilter;

    @Autowired
    private ServiceAuthenticationFilter serviceAuthenticationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	// NOTE:先经过token校验过滤器,再经过项目校验过滤器
        http.csrf().disable().authorizeRequests()
                .anyRequest().authenticated()
                .and().addFilterBefore(tokenAuthenticationFilter, BasicAuthenticationFilter.class)
                .addFilterAfter(projectAuthenticationFilter, TokenAuthenticationFilter.class)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

相关文章:

  • 【面试系列】Java开发--AI常见面试题
  • 微信小程序项目 video 组件失效问题,无法播放本地视频
  • 大模型WebUI:Gradio全解12——LangChain原理及其agent构建Gradio(1)
  • Percona XtraDB Cluster (PXC) 8.0的搭建
  • 【狂热算法篇】探秘图论之 Floyd 算法:解锁最短路径的神秘密码(通俗易懂版)
  • 为什么在 TypeScript 中需要使用 import type?——以 Babylon.js 为例
  • 无人机仿真、感知、规划
  • 千峰React:函数组件使用(2)
  • GIS地图、轨道交通与智能驾驶UI设计:未来交通的智能化探索
  • TPU(Tensor Processing Unit)详解
  • nnUNet V2修改网络——加入MultiResBlock模块
  • 1.25作业
  • 注意力机制进化史:从MHA到MoBA,新一代注意力机制的极限突破!
  • 计算机毕业设计SpringBoot+Vue.js母婴商城(源码+LW文档+PPT+讲解+开题报告)
  • Python zip 函数详解:用法、应用场景与高级技巧(中英双语)
  • 现代Web开发工具与技术全解析
  • 毕业项目推荐:基于yolov8/yolov5/yolo11的非机动车头盔佩戴检测识别系统(python+卷积神经网络)
  • 将产品照片(form.productPhotos)转为 JSON 字符串发送给后端
  • 【JavaEE进阶】图书管理系统 - 贰
  • C语言中的链表封装
  • 武汉楼市新政:二孩、三孩家庭购买新房可分别享受6万元、12万元购房补贴
  • 国泰海通合并后首份业绩报告出炉:一季度净利润增逾391%
  • 2025年度中国青年五四奖章暨新时代青年先锋奖评选揭晓
  • 媒体:黑话烂梗包围小学生,“有话好好说”很难吗?
  • 中使馆:奉劝菲方有关人士不要在台湾问题上挑衅,玩火者必自焚
  • 浙江官宣:五一假期,没电、没气、没油车辆全部免费拖离高速