26.JavaWeb-SpringSecurity安全框架

news2024/11/25 0:32:48

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() // 需要认证

    }

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

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

相关文章

Docker高级——网络配置

Docker网络 默认网络 安装 Docker 以后&#xff0c;会默认创建三种网络&#xff0c;可以通过 docker network ls 查看 [roottest ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 6f24f7cbfa10 bridge bridge local 2dc34a1c0f04 host host…

微信号长期没有使用或被回收?

7月17日&#xff0c;有网友表示自己之前申请了一个辅助账号作为树洞使用&#xff0c;如今登录的时候&#xff0c;弹出文字提示称“该微信号由于长期没有使用已被回收”。 腾讯客服回应表示&#xff0c;正常登录使用的微信号不会被系统回收&#xff0c;但对于长时间未登录的微信…

简用前后端的JSON格式注解:@DateTimeFormat、@JsonFormat、@JsonProperty

JsonFormat 【后端到前端】 在实体类属性上面使用JsonFormat注解了&#xff0c;要注意的是&#xff0c;它只会在声明返回类型为json时&#xff0c;比如使用ResponseBody返回json数据的时候&#xff0c;才会返回格式化的yyyy-MM-dd HH:mm:ss时间&#xff0c;如果直接使用Syste…

【分布式系统案例课】查询服务设计、计数栈选型、总结

查询服务设计 数据获取路径 两个问题考虑&#xff1a; 1、老数据归档的问题。 如果所有分钟小时级的数据一直存在这个DB当中&#xff0c;那么DB的存储空间会被不断的消耗&#xff0c;性能也会不断的下降。所以一旦小时天月的数据聚合完成&#xff0c;我们就可以将一些老的原始…

java: 错误: 不支持发行版本 5 java: 错误: 不支持发行版本8 java: 错误: 不支持发行版本17

&#x1f353;&#x1f353;原因 该错误表示你使用的Java编译器不支持Java 5版本的发行。Java版本的发行是根据不同的功能和语言变化来区分的。 要解决这个问题&#xff0c;你可以尝试以下几种方法&#xff1a; 检查编译器配置&#xff1a;确保你的IDE或编译器已正确配置为使…

Segment Tree 线段树算法(java)

线段树算法 Segment Tree 线段树算法代码演示 蓄水池算法 Segment Tree 线段树算法 什么是线段树算法&#xff1a; 线段树&#xff08;Segment Tree&#xff09;是一种基于树结构的数据结构&#xff0c;用于解决区间查询问题&#xff0c;例如区间最大值、最小值、区间和等。线段…

【数据结构】图解八大排序(下)

文章目录 一、前言二、快速排序1. hoare 版2. 挖坑法3. 前后指针法4. 快排的非递归实现5. 时空复杂度分析 三、归并排序1. 递归实现2. 非递归实现 四、计数排序 一、前言 在上一篇文章中&#xff0c;我们已经学习了五种排序算法&#xff0c;还没看过的小伙伴可以去看一下&…

C语言 —— 浮点类型详解及 IEEE754 规定

【C语言趣味教程】(3) 浮点类型&#xff1a;单精度浮点数 | 双精度浮点型 | IEEE754 标准 &#x1f517; 《C语言趣味教程》&#x1f448; 猛戳订阅&#xff01;&#xff01;&#xff01; ​—— 热门专栏《维生素C语言》的重制版 —— &#x1f4ad; 写在前面&#xff1a;这是…

Paragon NTFS2023中文最新版mac读写NTFS移动硬盘

当我们使用macOS系统将数据拷贝或写入NTFS格式磁盘时&#xff0c;却发现不能操作成功。搜索原因或解决方案时&#xff0c;许多网友推荐安装磁盘管理软件——Paragon NTFS for Mac。 往往大家都会有两个疑问&#xff0c;一是为什么非要使用NTFS格式的磁盘&#xff1f;二是为什么…

C/C++程序内存区域划分以及各区域的介绍

C/C程序内存区域划分 直接上图&#xff1a; 在这里插入图片描述 注&#xff1a;以下的说明均已VS2019为例 栈区&#xff08;stack&#xff09; 在执行函数时&#xff0c;函数内局部变量的存储单元都可以在栈上创建&#xff0c;函数执行结束时这些存储单元会自动释放。栈内…

用自己的数据拟合Sigmoid函数(Matlab平台)

%% 拟合sigmoid曲线 sigmoid (params, x) params(1) ./ (1 exp(-params(2) .* (x - params(3)))) params(4); %params(1) 是斜率参数&#xff0c;params(2) 是增长速率参数&#xff0c;params(3) 是 x 值的偏移参数&#xff0c;params(4) 是 y 值的偏移参数。 initialGuess…

剑指offer刷题笔记--Num51-60

1--数组中的逆序对&#xff08;51&#xff09; 主要思路&#xff1a; 基于归并排序&#xff0c;视频讲解参考&#xff1a;数组中的逆序对 #include <iostream> #include <vector>class Solution { public:int reversePairs(std::vector<int>& nums) {if(…

【力扣算法17】之 19. 删除链表的倒数第 N 个结点 python

文章目录 问题描述示例1示例2示例3提示 思路分析代码分析完整代码详细分析运行效果截图完结 问题描述 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例1 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例2…

加强安全防护,提升日志管理——探索EventLog Analyzer

导言&#xff1a; 在当今数字化时代&#xff0c;安全威胁和数据泄露已经成为各个组织和企业面临的严峻挑战。有效的日志管理和监控是确保网络安全的重要一环。本文将介绍EventLog Analyzer&#xff08;事件日志分析器&#xff09;这一强大工具&#xff0c;探索其在日志安全方面…

五、DQL-2.基本查询

一、数据准备 1、删除表employee&#xff1a; drop table employee; 2、创建表emp&#xff1a; 3、添加数据&#xff1a; 4、查看表数据&#xff1a; 【代码】 -- 查询数据--------------------------------------------------------- drop table emp;-- 数据准备-----------…

uni-app image加载错误 404 替换为默认图片

双层v-for 使用item修改 aitem.cat_icon || defaultPic绑定图片src属性为aitem.cat_icon 如果aitem.cat_icon的值为空字符串或undefined&#xff0c;那么默认图片defaultPic被显示出来当图片加载错误时,触发handleImageError方法,将aitem传进去 <!-- 页面--><view …

windows下mysql8定时备份,bat脚本编写,dos免密执行

前提&#xff1a;mysql8已经安装。 编写脚本copy_mysql_data.bat echo off set timestamp%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%%time:~6,2% set backupfileD:\ProgramData\MySQL\Backup\backup_%timestamp%.sql set mysqlpathD:\Program Files\MySQL\MyS…

成为一个年薪30W+的DFT工程师是一种什么体验?

一直以来&#xff0c;DFT都是数字IC设计行业中相对神秘的一个岗位。 你说他重要吧&#xff0c;并不是所有芯片设计公司都有这个岗位&#xff0c;你说他不重要吧&#xff0c;但凡芯片产品达到一定规模后&#xff0c;就必须设置DFT部门。 一、什么是DFT&#xff1f; DFT&#x…

1haclon 简单操作

文章目录 *读取图片 read_image(Image,claudia) *转换为灰度 rgb1_to_gray(Image,GrayImage)阈值分割 区域连接 获取最衣服 *读取图片 read_image(Image,claudia) *转换为灰度 select_shape (Connection, SelectedRegions, area, and, 40963.3, 44724.8) rgb1_to_gray(Image,Gr…

AI绘画 | 迷人武士美少女战士作品集

今天用Midjourney生成了质量极高的美少女武士后续会作为固定栏目来分享美图接下来请欣赏作品 提示词分享&#xff1a;1.an asian girl dressed in samurai style, in the style of anime aesthetic, trick of the eye paintings, dollcore, light red and black, resin, 8k, ex…