26.JavaWeb-SpringSecurity安全框架

1.SpringSecurity安全框架

        Spring Security是一个功能强大且灵活的安全框架,它专注于为Java应用程序提供身份验证(Authentication)、授权(Authorization)和其他安全功能。Spring Security可以轻松地集成到Spring框架中,为应用程序提供全面的安全性,包括但不限于以下功能:

  1. 身份验证(Authentication):Spring Security支持多种身份验证方式,如基于表单的身份验证、基于HTTP基本认证、基于OAuth2等。它可以轻松地集成到现有的用户认证系统中,也可以自定义认证逻辑。

  2. 授权(Authorization):Spring Security允许您定义资源的访问控制规则,以控制哪些用户有权访问哪些资源。您可以使用注解或配置来定义授权规则,从而实现细粒度的权限控制。

  3. 会话管理:Spring Security支持会话管理,可以处理会话超时、并发登录控制等问题,确保用户会话的安全性。

  4. CSRF(Cross-Site Request Forgery)保护:Spring Security可以防止跨站请求伪造攻击,保护应用程序免受此类攻击。

  5. 记住我(Remember Me):Spring Security提供了"记住我"功能,允许用户在下次访问时保持登录状态。

  6. 注销(Logout):Spring Security可以处理用户注销操作,包括清除会话信息、退出登录等。

  7. 安全事件和日志:Spring Security提供了安全事件监听器和日志,可以记录安全事件,便于监控和审计。

  8. OAuth2支持:Spring Security对OAuth2协议提供了强大的支持,可以轻松实现OAuth2认证和授权。

1.1 SpringSecurity配置类

        过编写配置类,可以定义身份验证方式、授权规则、会话管理等安全相关的设置

2.前后端不分离实现

3.前后端分离实现

3.1 身份验证

3.1.1 service层

        将提交的账号密码封装成authentication对象,然后通过认证管理器进行认证

@Slf4j
@Service
public class UserServiceImpl implements UserService {
    //认证管理器
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private UserMapper userMapper;
    @Override
    public User findByAccount(String account, String password) {
        //将账号密码封装成token对象
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(account,password);
        //调用security认证流程
        //只要此处得到Authentication就说明登陆成功
        Authentication authenticate = authenticationManager.authenticate(token);
        //获取user信息
        System.out.println(authenticate.getPrincipal());
        User user = (User) authenticate.getPrincipal();
        if(authenticate==null){
            log.debug("登陆失败");
            return null;
        }else{
            log.debug("登陆成功");
            return user;
        }
    }

    @Override
    public User findById(int id) {
        return userMapper.findById(id);
    }
}

3.1.2 controller层

        将用户信息返回给前端,将token、refreshtoken通过响应头返回给前端

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    @Resource
    private UserService userService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @PostMapping("/login")
    public ResponseResult<User> login(@RequestBody LoginVo loginVo, HttpSession session, HttpServletResponse response){
        User user = userService.findByAccount(loginVo.getAccount(),loginVo.getPassword());
DigestUtils.md5DigestAsHex(loginVo.getPassword().getBytes()).equals(user.getPassword())){
            //登陆成功
            //生成Token令牌
            String token = JWTUtil.generateToken(user.getId());
            //生成refreshToken
            String refreshtoken = UUID.randomUUID().toString();
            //放到redis中
            redisTemplate.opsForValue().set(refreshtoken,token,JWTUtil.REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS);

            //将token放到响应头中返回给前端(流行做法)
            response.setHeader("authorization",token);
            response.setHeader("refreshtoken",refreshtoken);
            //暴露头,浏览器不认识自定义的头,如果不暴露浏览器会自动屏蔽
            response.setHeader("Access-Control-Expose-Headers","authorization,refreshtoken");
            return new ResponseResult<>(200,"登陆成功",user);
    }
}

3.2 鉴权

        UsernamePasswordAuthenticationFilter之前手动的将authentication对象放到上下文中

3.2.1 创建过滤器继承OncePerRequestFilter

@Component
public class CustomAuthenticationFilter extends OncePerRequestFilter {

    @Resource
    private UserService userService;
    @Resource
    private RedisTemplate redisTemplate;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
        // 获取token、refreshtoken
        String token = request.getHeader("authorization");//Authentication
        String refreshtoken = request.getHeader("refreshtoken");

        if (token != null && token.length() !=0 ){
            // 校验refreshtoken、token
            // 校验refreshtoken:redis中是否有这个key  token是否为空 验证token是否与redis一致
            if (refreshtoken == null || !redisTemplate.hasKey(refreshtoken) || token == null || JWTUtil.verify(token) == TokenEnum.TOKEN_BAD || !token.equals(redisTemplate.opsForValue().get(refreshtoken))){
                // 非法、过期  去登录
                extracted(servletResponse);
                return;
            }
            // refreshtoken合法、有token、token合法且与redis一致 得到用户id放到session中
            request.getSession().setAttribute("uid", JWTUtil.getuid(token));

            // 如果过期
            if(JWTUtil.verify(token) == TokenEnum.TOKEN_EXPIRE){
                // 过期,重新生成token
                token = JWTUtil.generateToken(JWTUtil.getuid(token));

                // 修改redis中的数据
                redisTemplate.opsForValue().set(refreshtoken, token,JWTUtil.REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS);

                // 将新的token返回给前端
                HttpServletResponse response = (HttpServletResponse)servletResponse;
                response.setHeader("authorization", token);
                response.setHeader("Access-Control-Expose-Headers","authorization");
            }
            // 获取当前用户的id
            int uid = JWTUtil.getuid(token);

            // 通过用户id查询当前用户的角色、权限信息
            User user = userService.findById(uid);

            // 将用户信息封装成Authentication对象
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());

            // 将Authentication对象放到上下文
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        filterChain.doFilter(request, servletResponse);
    }

    private static void extracted(ServletResponse servletResponse) throws IOException {
        ResponseResult<Object> responseResult = new ResponseResult<>(403,"无法访问此界面,请登录",null);
        //转json
        String json = new ObjectMapper().writeValueAsString(responseResult);
        //设置响应头
        servletResponse.setContentType("application/json;charset=utf-8");
        servletResponse.getWriter().write(json);
    }
}

3.2.2 在配置类中将过滤器放在UsernamePasswordAuthenticationFilter之前

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .addFilterBefore(authenticationFilter, UsernamePasswordAuthenticationFilter.class);  
    }

3.3 异常处理

        在Spring Security中,异常处理是处理安全相关的异常情况,例如认证失败、访问拒绝等

        1.当用户未登录或者认证失败时,Spring Security 会调用 AuthenticationEntryPoint 的实现来处理该异常,并返回适当的响应给客户端

        2.当用户提供的凭据不正确或者认证失败时,Spring Security会抛出BadCredentialsException异常。可以通过实现AuthenticationFailureHandler接口来自定义认证失败的处理逻辑

        3.当用户访问了没有权限的资源时,Spring Security会抛出AccessDeniedException异常。可以通过实现AccessDeniedHandler接口来自定义访问拒绝的处理逻辑

3.3.1 未登录异常

@Component
@RestControllerAdvice
public class CustomNologinExceptionHandler implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.setHeader("Content-Type","application/json;charset=utf-8");
        httpServletResponse.getWriter().write(
                new ObjectMapper().writeValueAsString(
                        new ResponseResult<>(403,"你没有登录",false)));
    }
}

 3.3.1.1 配置类中处理未登录规则

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .authenticationEntryPoint(customNoLoginExceptionHandler)// 用户没登录怎么处理
    }

3.3.2 账号密码有误异常

        全局异常处理:一种在应用程序中统一处理异常的机制,它能够捕获应用程序中抛出的所有异常,并通过统一的处理逻辑进行处理,以便更好地向用户返回错误信息或执行其他操作

        注:全局异常处理捕获不到security中报的某些异常

@Slf4j
@RestControllerAdvice
public class AuthenticationExceptionHandler {
    @ExceptionHandler(BadCredentialsException.class)
    public ResponseResult<Boolean> handler(BadCredentialsException e){
        log.debug(e.getClass()+"");
        return new ResponseResult<>(403,"账号或密码有误",false);
    }
}

3.3.3 无权限异常

@Component
@RestControllerAdvice
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, org.springframework.security.access.AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.setHeader("Content-Type","application/json;charset=utf-8");
        httpServletResponse.getWriter().write(
                new ObjectMapper().writeValueAsString(
                        new ResponseResult<>(401,"你没有权限",false)));
    }
}

3.3.3.1 配置类中处理没权限规则

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .exceptionHandling()  // 指定异常处理
                .accessDeniedHandler(customAccessDeniedHandler) // 没权限怎么处理
    }

3.4 解决swagger冲突问题

3.4.1 在配置类中进行放行swagger静态资源

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/swagger-ui.html","/webjars/**","/v2/**","/swagger-resources/**")
                .permitAll() //不需要认证就能访问
                .anyRequest().authenticated() // 需要认证

    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

极值理论 EVT、POT超阈值、GARCH 模型分析股票指数VaR、条件CVaR:多元化投资组合预测风险测度分析...

全文链接&#xff1a;http://tecdat.cn/?p24182 本文用 R 编程语言极值理论 (EVT) 以确定 10 只股票指数的风险价值&#xff08;和条件 VaR&#xff09;&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 使用 Anderson-Darling 检验对 10 只股票的组合数据进行…

uniapp 小程序 联想地址搜索

效果图&#xff1a; qqmap-wx-jssdk.js下载 <template><view class"items"><view class"items-text">地址&#xff08;必填&#xff09;</view><input type"text" placeholder"搜索地址" maxlength&quo…

解决Missing cookie ‘JssionId‘ for method parameter of type String问题

错误描述如下所示&#xff1a; 上述错误是我在使用CookieValue注解&#xff0c;获取cookieID时出现的&#xff0c;错误原因是由于**CookieValue注解注解中的value值和浏览器中的cookie的jssionID不一致所导致的** 如下所示为浏览器中的CookieID的参数名 而我在注解中写的如下图…

自动化测试之数据驱动与关键字驱动

目录 1.录制/回放的神话 2.数据驱动的自动化测试框架 3.关键字驱动的自动化测试 初次接触自动化测试时&#xff0c;对数据驱动和关键字驱动不甚理解&#xff0c;觉得有点故弄玄须&#xff0c;不就是参数和函数其嘛&#xff01;其实其也体现了测试所不同与开发的一些特点&…

基于遗传算法的新能源电动汽车充电桩与路径选择MATLAB程序

主要内容&#xff1a; 根据城市间的距离&#xff0c;规划新能源汽车的行驶路径。要求行驶距离最短。 部分代码&#xff1a; %% 加载数据 %%遗传参数 load zby;%个城市坐标位置 NIND50; %种群大小 MAXGEN200; Pc0.9; %交叉概率 Pm0.2; %变异概率 GGAP0.…

postman 自动化测试

postman 自动化测试 0、写在前面1、变量引用1.1、如何在请求体中引用变量 2、变量设置2.1、测试需求场景描述&#xff1a;2.2、postman实战2.2.1、全局token的处理2.2.2、接口1的处理2.2.3、接口2的处理2.2.4、接口3的处理 3、测试结果展示 0、写在前面 在有些时候看官方文档 …

聚焦型光场相机基于立体视差的深度估计原理

聚焦型光场相机可以看作是主透镜将物面成了一个放大或者缩小的虚像&#xff0c;然后每个微透镜阵列对这个经过放大或者缩小的虚像进行二次成像后投影在了ccd平面&#xff0c;其中二次成像的过程可以比拟为一个虚拟阵列相机&#xff0c;利用MLA和主透镜的相关参数就可以以立体视…

Android系统开发-入门篇

参见&#xff1a;[视频教程] 写给应用开发的 Android Framework 教程——玩转 AOSP 篇之 Android 系统开发工具推荐 - 掘金 前置条件&#xff1a; android系统源码位于 linux 服务器&#xff0c;ssh 地址假如为&#xff1a;test172.1.10.2本机为windows 1、本机&#xff1a; 下…

访问Liunx文件系统

访问Liunx文件系统 识别文件系统和设备 存储管理概念 Linux服务器上文件按文件系统层次结构访问。该文件系统层次结构测试由系统可用的存储设备所提供的文件系统组装而来。每个文件系统都是一个已格式化的存储设备&#xff0c;可用于存储文件。 文件系统和挂载点 要让文件系…

【mysql】—— 数据库的操作

序言&#xff1a; 在上篇文章我已经对数据库进行了详细的介绍&#xff0c;接下来我们就将上手学习操作的细节了。本篇文章便带领大家去学习有关库操作的基本知识&#xff01;&#xff01;&#xff01; 目录 &#xff08;一&#xff09;库的操作 1、 创建数据库 2、字符集和…

HTML input text 常用事件

前言 用于记录开发中常用到的&#xff0c;快捷开发 简单实例 <input type"text" name"noSecretKeyJson" maxlength"200" />常用事件 oninput &#xff08;在用户输入时触发&#xff09;及案例 案例一&#xff1a;限制只允许输入数字…

C#基础--反射

反射 一、为什么学习反射 因为反射真的是无处不在&#xff0c;ORM、MVC、IOC、AOP、Attribute等等都会使用到反射。反射是程序员的快乐 二、什么是反射 Ilspy&#xff1a;逆向工程&#xff0c;可以吧DLL/Exe文件反编译回来 DLL/EXE 文件下包含Metadata和IL&#xff0c;IL是对…

跨文化合作:如何解决海外网红营销中的文化差异?

随着社交媒体的快速发展&#xff0c;海外网红营销已成为许多品牌和企业获取国际市场的有效方式。然而&#xff0c;由于不同国家和地区存在着独特的文化差异&#xff0c;如语言、价值观、习俗等&#xff0c;这也给品牌进行海外网红营销带来了一系列挑战。本文Nox聚星将和大家探讨…

WPF 自定义控件完成库容表盘显示效果

先看一下显示效果&#xff1a; 需要注意的地方有以下几点&#xff1a; 表盘的刻度分部&#xff0c;长刻度和短刻度显示。在数值80W时&#xff0c;需要更改刻度盘的颜色渐变。在数值80W时&#xff0c;更改库容总数背景的显示&#xff0c;也是颜色渐变。刻度盘控件属性定义&…

JVM中的堆和栈到底存储了什么

JVM数据区 先上一张Java虚拟机运行时数据区中堆、栈以及方法区存储数据的概要图&#xff0c;如下所示&#xff1a; 然后我们来具体解析一下堆和栈 堆 堆是存储时的单位&#xff0c;对于绝大多数应用来说&#xff0c;这块区域是 JVM 所管理的内存中最大的一块。线程共享&#…

JavaScript XHR、Fetch

1 前端数据请求方式 2 Http协议的解析 3 XHR的基本用法 4 XHR的进阶和封装 5 Fetch的使用详解 6 前端文件上传流程 早期的页面都是后端做好&#xff0c;浏览器直接拿到页面展示的&#xff0c;用到的是jsp、asp、php等等的语言。 这个叫做服务器端渲染SSR。 这里后端向前端…

[sqoop]导入数据

一、覆盖导入 例如维度表&#xff0c;每次导入的数据需要覆盖上次导入的数据。 hive-overwrite参数&#xff1a;实现覆盖导入 hive-import参数&#xff1a;表示向hive表导入 hive-table参数&#xff1a;指定目标hive库表 sqoop import \ --connect jdbc:mysql://hadoop1:3…

介绍性能压力测试的重要性

在当今数字化时代&#xff0c;软件和应用程序的性能对于用户体验和业务成功至关重要。为了确保系统在面临高负载和压力时能够正常运行&#xff0c;性能压力测试成为一项不可或缺的活动。本文将介绍性能压力测试的重要性。 性能压力测试是一种通过模拟实际场景中的负荷和用户访问…

前端两种实现轮播图方式

今天研究两种简单实现轮播图功能的方式。 目录 Layui实现轮播图 码云下载 提取静态文件 示例 注意 参数说明 改为轮播图 增加图片资源文件 轮播栏目修改 改为上下切换 切换事件 脚本中绑定改变事件 控制器查看 Swiper实现轮播图 下载swiper 下载到本地 加载sw…

EMC学习笔记(十七)PCB设计中的安规考虑

PCB设计中的安规考虑 1 概述2.安全标识2.1 对安全标示通用准则2.2 电击和能量的危险2.3 PCB上的熔断器2.4 可更换电池 3.爬电距离和电气间隙4.涂覆印制板4.1 PCB板的机械强度4.2 印制电路板的阻燃等级4.3 热循环试验与热老化试验4.4 抗电强度试验4.5 耐划痕试验 5.布线和供电 1…