SpringBoot3安全管理

news2024/11/24 18:53:34

标签:Security.登录.权限;

一、简介

SpringSecurity组件可以为服务提供安全管理的能力,比如身份验证、授权和针对常见攻击的保护,是保护基于spring应用程序的事实上的标准;

在实际开发中,最常用的是登录验证和权限体系两大功能,在登录时完成身份的验证,加载相关信息和角色权限,在访问其他系统资源时,进行权限的验证,保护系统的安全;

二、工程搭建

1、工程结构

2、依赖管理

starter-security依赖中,实际上是依赖spring-security组件的6.1.1版本,对于该框架的使用,主要是通过自定义配置类进行控制;

<!-- 安全组件 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>${spring-boot.version}</version>
</dependency>

三、配置管理

1、核心配置类

在该类中涉及到的配置非常多,主要是服务的拦截控制,身份认证的处理流程以及过滤器等,很多自定义的处理类通过该配置进行加载;

@EnableWebSecurity
@EnableMethodSecurity
@Configuration
public class SecurityConfig {

    /**
     * 基础配置
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        // 配置拦截规则
        httpSecurity.authorizeHttpRequests(authorizeHttpRequests->{
            authorizeHttpRequests
                    .requestMatchers(WhiteConfig.whiteList()).permitAll()
                    .anyRequest().authenticated();
        });
        // 禁用默认的登录和退出
        httpSecurity.formLogin(AbstractHttpConfigurer::disable);
        httpSecurity.logout(AbstractHttpConfigurer::disable);
        httpSecurity.csrf(AbstractHttpConfigurer::disable);

        // 异常时认证处理流程
        httpSecurity.exceptionHandling(exeConfig -> {
            exeConfig.authenticationEntryPoint(authenticationEntryPoint());
        });

        // 添加过滤器
        httpSecurity.addFilterAt(authTokenFilter(),CsrfFilter.class);
        return httpSecurity.build() ;
    }

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

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return new AuthExeHandler();
    }

    @Bean
    public OncePerRequestFilter authTokenFilter () {
        return new AuthTokenFilter();
    }

    /**
     * 认证管理
     */
    @Bean
    public AuthenticationManager authenticationManager() {
        return new ProviderManager(authenticationProvider()) ;
    }

    /**
     * 自定义用户认证流
     */
    @Bean
    public AbstractUserDetailsAuthenticationProvider authenticationProvider() {
        return new AuthProvider() ;
    }
}

2、认证数据源

UserDetailsService是加载用户特定数据的核心接口,编写用户服务类并实现该接口,提供用户信息和权限体系的数据查询和加载,作为用户身份识别的关键凭据;

@Service
public class UserService implements UserDetailsService {

    @Resource
    private UserBaseMapper userBaseMapper;
    @Resource
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        UserBase queryUser = geyByUserName(userName);
        if (Objects.isNull(queryUser)){
            throw new AuthException("该用户不存在");
        }
        List<GrantedAuthority> grantedAuthorityList = new ArrayList<>() ;
        grantedAuthorityList.add(new SimpleGrantedAuthority(queryUser.getUserRole())) ;
        return new User(queryUser.getUserName(),queryUser.getPassWord(),grantedAuthorityList);
    }

    public int register (UserBase userBase){
        if (!Objects.isNull(userBase)){
            userBase.setPassWord(passwordEncoder.encode(userBase.getPassWord()));
            userBase.setCreateTime(new Date()) ;
            return userBaseMapper.insert(userBase) ;
        }
        return 0 ;
    }

    public UserBase getById (Integer id){
        return userBaseMapper.selectById(id) ;
    }

    public UserBase geyByUserName (String userName){
        List<UserBase> userBaseList = new LambdaQueryChainWrapper<>(userBaseMapper)
                .eq(UserBase::getUserName,userName).last("limit 1").list();
        if (userBaseList.size() > 0){
            return userBaseList.get(0) ;
        }
        return null ;
    }
}

3、认证流程

自定义用户名和密码的身份令牌认证逻辑,基于用户名Username从上面的用户服务类中加载数据并校验,在验证成功后将用户的身份令牌返回给调用者;

@Component
public class AuthProvider extends AbstractUserDetailsAuthenticationProvider {
    private static final Logger log = LoggerFactory.getLogger(AuthProvider.class);
    
    @Resource
    private UserService userService;
    @Resource
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    protected void additionalAuthenticationChecks(
            UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        User user = (User) userDetails;
        String loginPassword = authentication.getCredentials().toString();
        log.info("user:{},loginPassword:{}",user.getPassword(),loginPassword);
        if (!passwordEncoder.matches(loginPassword, user.getPassword())) {
            throw new AuthException("账号或密码错误");
        }
        authentication.setDetails(user);
    }
    @Override
    protected UserDetails retrieveUser(
            String username, UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        log.info("username:{}",username);
        return userService.loadUserByUsername(username);
    }
}

4、身份过滤器

通过继承OncePerRequestFilter抽象类,实现用户身份的过滤器,如果不是白名单请求,需要验证令牌是否正确有效,SecurityContextHolder默认状态下使用ThreadLocal存储信息;

@Component
public class AuthTokenFilter extends OncePerRequestFilter {
    @Resource
    private AuthTokenService authTokenService ;
    @Resource
    private AuthExeHandler authExeHandler ;

    @Override
    protected void doFilterInternal(@Nonnull HttpServletRequest request,
                                    @Nonnull HttpServletResponse response,
                                    @Nonnull FilterChain filterChain) throws ServletException, IOException {
        String uri = request.getRequestURI();
        if (Arrays.asList(WhiteConfig.whiteList()).contains(uri)){
            // 如果是白名单直接放行
            filterChain.doFilter(request,response);
        } else {
            String token = request.getHeader("Auth-Token");
            if (Objects.isNull(token) || token.isEmpty()){
                // Token不存在,拦截返回
                authExeHandler.commence(request,response,null);
            } else {
                Object object = authTokenService.getToken(token);
                if (!Objects.isNull(object) && object instanceof User user){
                    UsernamePasswordAuthenticationToken authentication =
                            new UsernamePasswordAuthenticationToken(user, null,user.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    filterChain.doFilter(request,response);
                } else {
                    // Token验证失败,拦截返回
                    authExeHandler.commence(request,response,null);
                }
            }
        }
    }
}

四、核心功能

1、登录退出

自定义登录退出两个接口,基于用户名和密码执行上述的身份认证流程,如果认证成功则返回用户的身份令牌,在请求「非」白名单接口时需要在请求头中Auth-Token:token携带该令牌,在退出时会清除身份信息;

@Service
public class LoginService {

    private static final Logger log = LoggerFactory.getLogger(LoginService.class);

    @Resource
    private AuthTokenService authTokenService ;
    @Resource
    private AuthenticationManager authenticationManager;

    public String doLogin (UserBase userBase){
        AbstractAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                userBase.getUserName().trim(), userBase.getPassWord().trim());
        Authentication authentication = authenticationManager.authenticate(authToken) ;
        User user = (User) authentication.getDetails();
        return authTokenService.createToken(user) ;
    }

    public Boolean doLogout (String authToken){
        SecurityContextHolder.clearContext();
        return authTokenService.deleteToken(authToken) ;
    }
}

@Service
public class AuthTokenService {

    private static final Logger log = LoggerFactory.getLogger(AuthTokenService.class);
    @Resource
    private RedisTemplate<String,Object> redisTemplate ;

    public String createToken (User user){
        String userName = user.getUsername();
        String token = DigestUtils.md5DigestAsHex(userName.getBytes());
        log.info("user-name:{},create-token:{}",userName,token);
        redisTemplate.opsForValue().set(token,user,10, TimeUnit.MINUTES);
        return token ;
    }

    public Object getToken (String token){
        return redisTemplate.opsForValue().get(token);
    }

    public Boolean deleteToken (String token){
        return redisTemplate.delete(token);
    }
}

2、权限校验

UserWeb类中提供用户的注册接口,在用户表中创建两个测试用户:admin对应ROLE_Admin角色,user对应ROLE_User角色,验证如下几个接口的权限控制;

select接口不需要鉴权,拦截器放行即可访问;getUser接口校验ROLE_User角色;getAdmin接口校验ROLE_Admin角色;query接口校验两个角色中的任意一个即可;

两个不同用户登录获取到各自的身份令牌,使用不同的令牌请求接口,在PreAuthorize验证通过后才可以正常访问;

@RestController
public class UserWeb {

    @Resource
    private UserService userService ;

    @PostMapping("/register")
    public String register (@RequestBody UserBase userBase){
        return "register-"+userService.register(userBase) ;
    }

    @GetMapping("/select/{id}")
    public UserBase select (@PathVariable Integer id){
        return userService.getById(id) ;
    }

    @PreAuthorize("hasRole('User')")
    @GetMapping("/user/{id}")
    public UserBase getUser (@PathVariable Integer id){
        return userService.getById(id) ;
    }

    @PreAuthorize("hasRole('Admin')")
    @GetMapping("/admin/{id}")
    public UserBase getAdmin (@PathVariable Integer id){
        return userService.getById(id) ;
    }

    @PreAuthorize("hasAnyRole('User','Admin')")
    @GetMapping("/query/{id}")
    public UserBase query (@PathVariable Integer id){
        return userService.getById(id) ;
    }
}

五、参考源码

文档仓库:
https://gitee.com/cicadasmile/butte-java-note

源码仓库:
https://gitee.com/cicadasmile/butte-spring-parent

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/874769.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BC136 KiKi去重整数并排序

给定一个整数序列&#xff0c;KiKi想把其中的重复的整数去掉&#xff0c;并将去重后的序列从小到大排序输出。 输入描述 第一行&#xff0c;输入一个整数n&#xff0c;表示序列有n个整数。 第二行输入n个整数&#xff08;每个整数大于等于1&#xff0c;小于等于1000&#xf…

Azure资源命名和标记决策指南

参考 azure创建虚拟机在虚拟机中选择编辑标签&#xff0c;并添加标记&#xff0c;点击应用 3.到主页中转到所有资源 4. 添加筛选器并应用 5.查看结果&#xff0c;筛选根据给服务器定义的标签筛选出结果。 参考链接: https://learn.microsoft.com/zh-cn/azure/cloud-adoption…

Java接口压力测试—如何应对并优化Java接口的压力测试

导言 在如今的互联网时代&#xff0c;Java接口压力测试是评估系统性能和可靠性的关键一环。一旦接口不能承受高并发量&#xff0c;用户体验将受到严重影响&#xff0c;甚至可能导致系统崩溃。因此&#xff0c;了解如何进行有效的Java接口压力测试以及如何优化接口性能至关重要…

3张照片打造专属形象!酷蛙FaceChain解密个人写真开源项目,人人AIGC!

一、背景说明 各类AI写真软件由于其精准的个人形象精美的生成效果引爆了朋友圈传播&#xff0c;证件照满足了用户刚需&#xff0c;古装照等风格照满足了用户“美照”的需求。 酷蛙FaceChain开源项目团队推出了开源版本&#xff0c;希望结合开源社区开发者的力量&#xff0c;可…

Linux之openoffice安装

一、openoffice简介 OpenOffice是一个开源的办公软件套件&#xff0c;包含了文本编辑器、电子表格、演示文稿、数据库和绘图等应用程序。它可以在多个操作系统上运行&#xff0c;包括Windows、Mac OS X和Linux等。OpenOffice的目标是提供一个完全免费、功能齐全的办公软件套件&…

【LeetCode】【数据结构】栈与队列必刷OJ题

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言&#xff1a; 【LeetCode】20.有效的括号&#xff08;栈的…

1289. 下降路径最小和 II

题目描述&#xff1a; 给你一个 n x n 整数矩阵 grid &#xff0c;请你返回 非零偏移下降路径 数字和的最小值。 非零偏移下降路径 定义为&#xff1a;从 grid 数组中的每一行选择一个数字&#xff0c;且按顺序选出来的数字中&#xff0c;相邻数字不在原数组的同一列。 示例&am…

企业级备份 - 让您的数据安全防线坚韧如山

产品定义 数据威胁无处不在&#xff0c;不论数据处于何地&#xff0c;以何种形态体现&#xff0c;数据安全都至关重要。备份作为数据安全的最后一道防线&#xff0c;需要慎之又慎。针对数据备份与恢复的企业级应用&#xff0c;联想凌拓 (Lenovo NetApp) 与华睿泰 (Veritas) 共同…

【C++】速识string

一、创建string对象 1、文档 2、常用 并不是所有的用法都需要熟记于心&#xff0c;我们只需记住常用的即可&#xff0c;对于并不常用的&#xff0c;我们可以在用到的时候查看文档学习使用。 void Test1() {string s1;string s2("Hello World");s1 "Hello …

Android 10.0 禁止二次展开QuickQSPanel设置下拉QSPanel高度

1.前言 在10.0的系统定制化需求中,在进行systemui的ui定制开发中,有些需要对原生systemui下拉状态栏中的二次展开QSPanel修改成 一次展开禁止二次展开,所以就需要修改QuickQSpanel的高度,然后在QuickQsPanel做定制,然后禁止二次展开就可以了 如图: 2.禁止二次展开Quic…

ubuntu下FFmpeg安装和使用以及CMakeLists.txt模板

sudo apt install ffmpeg sudo apt-get install libavfilter-devcmakelist模板 CMakeLists.txt cmake_minimum_required(VERSION 3.16) project(ffmpeg_demo)# 设置ffmpeg依赖库及头文件所在目录&#xff0c;并存进指定变量 set(ffmpeg_libs_DIR /usr/lib/x86_64-linux-gnu) …

【Windows 常用工具系列 7 -- 禁用win10自带的微软输入法】

文章目录 1.1 输入法配置 1.1 输入法配置 首先建议采用搜狗输入法&#xff0c;这个更符合中国人的输入习惯&#xff0c;强烈建议删除微软自带的中文输入法&#xff0c;删除方式见&#xff1a; 点击电脑主屏幕左下角开始图标&#xff0c;选择设置&#xff1a; 选择“时间和语…

AutoxJS脚本保姆级教程

目录 引言 准备工作 脚本开发 邮件提示&#xff08;不使用邮件推送的可以跳过这步&#xff09; 节假日判断&#xff08;不需要判断节假日的可以跳过&#xff09; 问题及技巧归总 JS语法错误&#xff1a;软件更新 按钮或组件无法找到 使用定时器等待组件出现 root环境下…

Scratch 之 单个角色模糊特效

想给单独一个角色用模糊特效&#xff0c;怎么办&#xff01;&#xff01;&#xff01; 完全不用慌&#xff0c;最近&#xff0c;我做出了超好用的个体模糊特效&#xff0c;用起来超级简单&#xff0c;接下来进入讲解。 1.原理 前几天&#xff0c;我闲来无事&#xff0c;摘下眼镜…

【2023年11月第四版教材】《第4章-信息系统管理之管理要点(第四版新增章节)(第二部分)》

信息系统管理之管理要点&#xff08;第四版新增章节&#xff09;&#xff08;第二部分&#xff09; 2 管理要点2.1 数据管理能力成熟度评估模型DCMM详细表格简要表格 2.2 组织的管理成熟度2.3 能力模型2.4 智能运维能力框架2.5 安全保护等级 2 管理要点 2.1 数据管理能力成熟度…

华为云classroom赋能--Devstar使应用开发无需从零开始

华为云DevStar为开发者提供业界主流框架代码初始化能力&#xff0c;通过GUI、API、CLI等多种方式&#xff0c;将按模板生成框架代码的能力推送至用户桌面。同时基于华为云服务资源、成熟的DevOps开发工具链和面向多场景的众多开发模板&#xff0c;提供一站式创建代码仓、自动生…

js的练习

这里写目录标题 工具代码运行结果 工具 HBuilder X 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><script>window.onload function() // 需要在body加载完成之后&#xff0c;才能通过docu…

HCIP BGP小综合

BGP小综合 AS配置AS1AS2 中的小自治系统64512AS2 中的小自治系统64513AS3 测试 首先该实验分成三个AS&#xff0c;AS2里面有联邦&#xff0c;所以配置顺序 要先将IBGP通&#xff0c;然后配置AS1,AS3和联邦 AS配置 AS1 R1 # bgp 1router-id 1.1.1.1peer 12.1.1.2 as-number …

C++ 网络编程项目fastDFS分布式文件系统(二)-redis部分

目录 1. 数据库类型 1.1 基本概念 1.2 关系/非关系型数据库搭配使用 2. Redis 2.1 基本知识点 2.2 redis常用命令 - String类型 - List类型 - Set类型 - SortedSet 类型 - Hash类型 Key 相关的命令 2.3 redis配置文件 2.4 redis数据持久化 3 hiredis的使用 1. 数据…

人类:我觉得1+1=956446,你觉得呢?大模型:啊对对对

大模型太「听话」了怎么办&#xff1f; 大型语言模型&#xff08;LLM&#xff09;的自然语言理解与生成能力一直备受称赞&#xff0c;特别是 ChatGPT 等对话式语言模型能够与人类流畅、自然地进行多轮对话。然而&#xff0c;最近一篇 Google DeepMind 的论文研究发现 LLM 普遍存…