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

news/2025/2/22 6:50:52

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();
    }
}

http://www.niftyadmin.cn/n/5861838.html

相关文章

学习threejs,使用MeshBasicMaterial基本网格材质

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.MeshBasicMaterial 二…

前端ES面试题及参考答案

let/const 与 var 的区别&#xff1f;TDZ 是什么&#xff1f; 在 ES6 中引入了let和const&#xff0c;它们与var存在多方面区别。 作用域方面&#xff1a;var具有函数作用域&#xff0c;意味着在函数内部使用var声明的变量&#xff0c;在整个函数体中都可以访问。例如&#xff…

【Python】打造自己的HTTP server

词汇汇总 CRLF 指的是换行和回车\r\n 教程 ./your_program.sh #启动自己的服务curl -v http://localhost:4221#开启另一个终端 测试HTTP response An HTTP response is made up of three parts, each separated by a CRLF (\r\n): Status line. Zero or more headers, each …

机器学习课程的常见章节结构

以下是机器学习课程的常见章节结构&#xff0c;结合了搜索结果中的信息&#xff1a; 1. 机器学习基础知识 机器学习的定义与分类 监督学习、无监督学习、半监督学习、强化学习 机器学习的产生与发展 机器学习的历史与现代应用 经验误差与过拟合 过拟合与欠拟合的概念及解决…

HBase的安全性考量:保护你的数据不受威胁

HBase的安全性考量&#xff1a;保护你的数据不受威胁 数据安全的重要性 在大数据时代&#xff0c;数据是企业最宝贵的资产之一。随着数据量的不断增加和应用场景的多样化&#xff0c;如何确保数据的安全性成为了每一个开发者和企业的首要任务。今天&#xff0c;我们将深入探讨…

国产编辑器EverEdit - 如何在EverEdit中管理工程?

1 工程管理 1.1 应用场景 用户创建工程后&#xff0c;会涉及到工程的管理 &#xff0c;比如&#xff1a;打开工程、关闭工程等 1.2 使用方法 1.2.1 打开工程 单击主菜单工程 -> 打开工程&#xff0c;会弹出打开对话框&#xff0c;用户在对话框中选择需要打开的工程文件即…

C#初级教程(1)——C# 与.NET 框架:探索微软平台编程的强大组合

图片来源&#xff1a; https://www.lvhang.site/docs/dotnettimeline 即梦AI - 一站式AI创作平台 一、历史发展脉络 在早期的微软平台编程中&#xff0c;常用的编程语言有 Visual Basic、C、C。到了 20 世纪 90 年代末&#xff0c;Win32 API、MFC&#xff08;Microsoft Found…

高级SQL技术在Python项目中的应用:ORM与深度性能优化

引言 在现代Python项目开发中,数据库交互远不止是数据的简单存取,它已成为构建高性能、可维护应用的核心瓶颈和关键能力所在。 仅仅依赖基础SQL查询,虽然入门简单,却难以应对日益增长的应用挑战。这些挑战主要体现在以下几个方面: 性能瓶颈: 数据量剧增: 从百万到数十亿乃…