实战项目ssm权限系统 2-自定义springsecurity组件实现认证授权

news2024/11/25 16:36:59

 一  springsecurity 作用

1.1 springsecurity

Spring security是spring家族的一个安全性框架,主要是用来进行用户认证(Authentication)用户授权(Authorization)的框架。

用户认证:验证用户登录是否合法

用户授权:登录成功后用户具有哪些权限,可以访问哪些资源。

Spring Security进行认证和鉴权的时候,就是利用的一系列的Filter链来进行拦截的。其中最重要的两个过滤器:

UsernamePasswordAuthenticationFilter负责登录认证,FilterSecurityInterceptor负责权限授权

1.2 认证核心组件

通过 SecurityContext 来获取Authentication,SecurityContext就是我们的上下文对象!这个上下文对象则是交由 SecurityContextHolder 进行管理,你可以在程序任何地方使用它:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder原理非常简单,就是使用ThreadLocal来保证一个线程传递同一个对象。

核心组件:

1、Authentication:存储了认证信息,代表当前登录用户
2、SeucirtyContext:上下文对象,用来获取Authentication
3、SecurityContextHolder:上下文管理对象,用来在程序任何地方获取SecurityContext

Authentication中是什么信息呢:
1、Principal:用户信息,没有认证时一般是用户名,认证后一般是用户对象
2、Credentials:用户凭证,一般是密码
3、Authorities:用户权限

1.3 认证流程

1.将最后封装好的认证信息 Authentication,放入:

Authentication authentication = SecurityContextHolder.getContext().setAuthentication();


 二  工程搭建

2.1 工程结构

 2.2 spring-security搭建

2.2.1 pom文件

 <dependency>
            <groupId>com.atguigu</groupId>
            <artifactId>common-util</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>com.atguigu</groupId>
            <artifactId>model</artifactId>
            <version>1.0</version>
        </dependency>
        <!-- Spring Security依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>provided </scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <scope>provided </scope>
        </dependency>

说明:依赖包(spring-boot-starter-security)导入后,Spring Security就默认提供了许多功能将整个应用给保护了起来:

​ 1、要求经过身份验证的用户才能与应用程序进行交互

​ 2、创建好了默认登录表单

​ 3、生成用户名为user的随机密码并打印在控制台上

​ 4、CSRF攻击防护、Session Fixation攻击防护

​ 5、等等等等......

2.2.2 添加安全配置类

package com.atguigu.system.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


@Configuration
@EnableWebSecurity //@EnableWebSecurity是开启SpringSecurity的默认行为
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


}

2.2.3 service-system模块引入spring-security模块

在service-system模块引入spring-security模块,spring-security就对service-system模块的资源进行保护起来。使用spring-security的默认的认证授权保护机制。

2.2.4 访问测试

在浏览器访问:http://localhost:8800/admin/system/sysRole/findAll

自动跳转到了登录页面

 

密码在项目启动的时候在控制台会打印,注意每次启动的时候密码都会发生变化!

 输入用户名,密码,成功访问到controller方法并返回数据,说明Spring Security默认安全保护生效。

在实际开发中,这些默认的配置是不能满足我们需要的,我们需要扩展Spring Security组件,完成自定义配置,实现我们的项目需求。

三  认证的配置

3.1 本案例认证配置流程

Spring Security默认的认证方式就是UsernamePasswordAuthenticationFilter这个过滤器中进行认证的,该过滤器负责认证逻辑。

总结执行顺序:

1.先执行TokenAuthenticationFilter (token验证过滤器),
2.其次执行TokenLoginFilter (认证过滤器),
3.最后执行 UserDetailsServiceImpl(具体模块中查询用户名和密码,权限等信息)
 3.1如果验证合法返回到TokenLoginFilter类中success方法中进行后面操作,
 3.2如果验证不合法返回到TokenLoginFilter类中unsuccess方法中进行返回提示验证失败

3.2 step1:自定义密码组件

自定义加密组件 实现其passwordEncoder接口

 3.3  step2:自定义user类

自定义用户类,需要实现springsecurity框架中包含的User类。

3.4  step3:自定义查询数据库用户的信息

pring security 默认接口是UserDetailsService,里面只有一个方法:loadUserByUsername()

public interface UserDetailsService {
    /**
     * 根据用户名获取用户对象(获取不到直接抛异常)
     */
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

自定义用户查询用信息实现类,需要实现UserDetailsService接口

2.我们需要自定义自己的业务逻辑实现:通过用户名查询用户信息。写在service-system模块中。

 代码:

@Component
public class UserDetailsServiceImpl implements UserDetailsService {
 
    @Autowired
    private SysUserService sysUserService;
 
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser sysUser = sysUserService.getByUsername(username);
        if(null == sysUser) {
            throw new UsernameNotFoundException("用户名不存在!");
        }
 
        if(sysUser.getStatus().intValue() == 0) {
            throw new RuntimeException("账号已停用");
        }
        return new CustomUser(sysUser, Collections.emptyList());
    }
}

3.5 step4:自定义认证过滤

登录过滤器,继承UsernamePasswordAuthenticationFilter,对用户名密码进行登录校验

1.设置构造函数;2.设置登录认证;3.登录成功后,处理逻辑;3.登录失败后,处理逻辑。

 2.代码

/**
 * <p>
 * 登录过滤器,继承UsernamePasswordAuthenticationFilter,对用户名密码进行登录校验
 * </p>
 *
 */
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
 
    public TokenLoginFilter(AuthenticationManager authenticationManager) {
        this.setAuthenticationManager(authenticationManager);
        this.setPostOnly(false);
        //指定登录接口及提交方式,可以指定任意路径
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login","POST"));
    }
 
    /**
     * 登录认证
     * @param req
     * @param res
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
            throws AuthenticationException {
        try {
            LoginVo loginVo = new ObjectMapper().readValue(req.getInputStream(), LoginVo.class);
 
            Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
            return this.getAuthenticationManager().authenticate(authenticationToken);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
 
    }
 
    /**
     * 登录成功
     * @param request
     * @param response
     * @param chain
     * @param auth
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication auth) throws IOException, ServletException {
        CustomUser customUser = (CustomUser) auth.getPrincipal();
        String token = JwtHelper.createToken(customUser.getSysUser().getId(), customUser.getSysUser().getUsername());
 
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        ResponseUtil.out(response, Result.ok(map));
    }
 
    /**
     * 登录失败
     * @param request
     * @param response
     * @param e
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException e) throws IOException, ServletException {
        
        if(e.getCause() instanceof RuntimeException) {
            ResponseUtil.out(response, Result.build(null, 204, e.getMessage()));
        } else {
            ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_MOBLE_ERROR));
        }
    }
}

3.6 step5:响应字符串

 

2.代码


public class ResponseUtil {
 
    public static void out(HttpServletResponse response, Result r) {
        ObjectMapper mapper = new ObjectMapper();
        response.setStatus(HttpStatus.OK.value());
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        try {
            mapper.writeValue(response.getWriter(), r);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.7 step6:自定义 解析token过滤器 

因为用户登录状态在token中存储在客户端,所以每次请求接口请求头携带token, 后台通过自定义token过滤器拦截解析token完成认证并填充用户信息实体。

解析验证token过滤器继承于OncePerRequestFilter。验证token是否合法。

 代码

package com.atguigu.system.filter;

import com.alibaba.fastjson.JSON;
import com.atguigu.common.result.Result;
import com.atguigu.common.result.ResultCodeEnum;
import com.atguigu.common.utils.JwtHelper;
import com.atguigu.common.utils.ResponseUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

//认证解析过滤器
public class TokenAuthenticationFilter extends OncePerRequestFilter {
    private RedisTemplate redisTemplate;

    public TokenAuthenticationFilter(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        logger.info("uri:"+request.getRequestURI());
        //如果是登录接口,直接放行
        if("/admin/system/index/login".equals(request.getRequestURI())) {
            chain.doFilter(request, response);
            return;
        }

        if("/prod-api/admin/system/index/login".equals(request.getRequestURI())) {
            chain.doFilter(request, response);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = getAuthenticationByDefine(request);
        if(null != authentication) {
            SecurityContextHolder.getContext().setAuthentication(authentication);
            chain.doFilter(request, response);
        } else {
            ResponseUtil.out(response, Result.build(null, ResultCodeEnum.PERMISSION));
        }
    }

    private UsernamePasswordAuthenticationToken getAuthenticationByDefine(HttpServletRequest request) {
        // token置于header里
        String token = request.getHeader("token");
        logger.info("token:"+token);
        if (!StringUtils.isEmpty(token)) {
            String useruame = JwtHelper.getUsername(token);
            logger.info("useruame:"+useruame);
            if (!StringUtils.isEmpty(useruame)) {
                String authoritiesString =
                        (String) redisTemplate.opsForValue().get(useruame);
                List<Map> mapList = JSON.parseArray(authoritiesString, Map.class);
                List<SimpleGrantedAuthority> authorities = new ArrayList<>();
                for (Map map : mapList) {
                    authorities.add(new SimpleGrantedAuthority((String)map.get("authority")));
                }
                return new UsernamePasswordAuthenticationToken(useruame, null, authorities);
            }
        }
        return null;
    }
}

3.8 step7:自定义 webSecurityConfig全局配置类 

 3.9 测试验证

在浏览器访问:http://localhost:8800/admin/system/sysRole/findAll

 

 

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

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

相关文章

Linux ARM64架构 动态替换 altinstructions

文章目录 简介一、altinstructions节1.1 .altinstructions1.2 .rela.altinstructions 二、内核模块重定位源码分析参考资料 简介 在内核开发中&#xff0c;有时需要对内核代码进行修补&#xff0c;以解决bug、优化性能或引入新功能。替代指令&#xff08;altinstructions&…

云原生K8S------Yaml文件详解

目录 一&#xff1a;K8S支持的文件格式 1&#xff0c;yaml和json的主要区别 2&#xff0c;YAML语言格式 二&#xff1a;yuml 1、查看 api 资源版本标签 2、写一个yaml文件demo 3、创建service服务对外提供访问并测试 4、详解k8s中的port 三&#xff1a;文件生成 1、kubec…

如果你需要使用重试机制,请使用Spring官方的Spring Retry

Spring Retry 是 Spring Framework 中的一个模块&#xff0c;提供了一种简单的方式来在应用程序中实现重试机制。 在应用程序中&#xff0c;如果遇到了一些不可避免的错误&#xff0c;比如网络连接失败、数据库连接失败等&#xff0c;我们通常需要对这些错误进行重试&#xff…

搭建日志服务器Rsyslog

Rsyslog介绍 Rsyslog的全称是 rocket-fast system for log&#xff0c;它提供了高性能&#xff0c;高安全功能和模块化设计。rsyslog能够接受从各种各样的来源&#xff0c;将其输入&#xff0c;输出的结果到不同的目的地。rsyslog可以提供超过每秒一百万条消息给目标文件。 特…

Qt+C++实现灯带动画运动位置变换移动跑马灯图片轮播

程序示例精选 QtC实现灯带动画运动位置变换移动跑马灯图片轮播 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<QtC实现灯带动画运动位置变换移动跑马灯图片轮播>>编写代码&…

Vue项目npm run dev 启动报错TypeError: Cannot read property ‘upgrade‘ of undefined

vue项目启动报错 TypeError: Cannot read property upgrade of undefined 由于我的vue.config.js文件 里面的代理target为空导致的 修改&#xff1a; 结果就可以正常运行了 参考原文&#xff1a; vue项目运行时报Cannot read property ‘upgrade’ of undefined错误_cannot r…

【Docker】Docker的应用场景,Docker 的优点,Ubuntu Docker 安装,使用 Shell 脚本进行安装

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

【福建事业单位-资料分析】01 速算技巧-基期与现期

【福建事业单位-资料分析】01 速算技巧-基期与现期 一、速算技巧&#xff08;基于选项的速算&#xff09;1.1 计算类别——截位直除练习总结 二、速算技巧-比较类-分数比较2.1 一大一小&#xff08;一大/一小&#xff09;2.2 同大同小①分子分母都变大&#xff0c;保留两位直接…

智橙PDM系统:图文档与物料数据同时管理编辑系统

在当今快速变化的商业环境中&#xff0c;数字化转型已经成为企业不可或缺的一部分。产品数据的高效管理和协作变得尤为重要&#xff0c;而智橙的PDM系统&#xff08;产品数据管理系统&#xff09;为企业提供了无限协作的全新维度。 无限协作&#xff0c;创新无限 智橙PDM系统瞄…

QListView的使用(正逆序插入)

首先在介绍QListView之前&#xff0c;先说一下QListView和QListWidget的区别&#xff1a; 1、QListView是model&#xff08;模型/视图&#xff09;表格类型&#xff0c;QListWidget它是Item表格类型。 2、QListView使用较复杂&#xff0c;一般需要配合数据模型QAbstractListMod…

【Linux】公网环境下Ubuntu系统SSH远程树莓派

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于恒川的日常汇报系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏C语言初…

Nginx与Tomcat的区别,什么是HTTP服务器(处理静态资源的服务器),什么是处理动态资源的服务器

Nginx和Tomcat都是常用的Web服务器&#xff0c;但它们的主要作用不同。Nginx是一个HTTP服务器&#xff0c;反向代理服务器和通用TCP/UDP代理服务器。它通常用于静态内容、媒体流和负载均衡。在高流量和高并发负载下&#xff0c;Nginx表现更出色&#xff0c;并且能够轻松处理静态…

解决android studio妙明奇妙出现的模拟器

1&#xff0c;查看设备 adb devices 2&#xff0c; adb命令断开指定的设备 要断开ADB与特定设备的连接&#xff0c;可以使用以下命令&#xff1a; adb disconnect <设备ID> 将 <设备ID> 替换为您要断开连接的设备的实际ID。设备ID可以在运行 adb devices 命令…

C语言刷题------(2)

C语言刷题——————&#xff08;2&#xff09; 刷题网站&#xff1a;题库 - 蓝桥云课 (lanqiao.cn) First Question&#xff1a;时间显示 题目描述 小蓝要和朋友合作开发一个时间显示的网站。 在服务器上&#xff0c;朋友已经获取了当前的时间&#xff0c;用一个整数表…

Ansible的安装和配置

安装和配置 Ansible 安装所需的软件包 创建名为 /home/greg/ansible/inventory 的静态清单文件&#xff0c;以满足以下要求&#xff1a; 172.25.250.9 是 dev 主机组的成员 172.25.250.10 是 test 主机组的成员 172.25.250.11 和 172.25.250.12 是 prod 主机组的成员 172.2…

电测知识分享——10分钟学会!超火网络应用测试教程来了,火速收藏

在当今的网络应用中&#xff0c;当属以太网最流行&#xff0c;特别是终端电脑的连接几乎是以太网的天下&#xff0c;即使无线网卡最终也是以太网协议。终端网络分为有线以太网和无线WF,目前都形成了IEEE的标准。 今天&#xff0c;我们就来学习一下&#xff0c;1000M BASE-T&am…

war和war exploded

war和war exploded的区别 war模式&#xff1a;将WEB工程以包的形式上传到服务器 &#xff1b; war exploded模式&#xff1a;将WEB工程以当前文件夹的位置关系上传到服务器&#xff1b;>> war包是自己打包生成的&#xff0c;如pom文件中<packaging>war</packag…

【设计模式】责任链的基本概念及使用Predicate灵活构造校验链

文章目录 1. 概述1.1.背景1.2.责任链模式的概念 2.责任链的基本写法2.1.链表实现2.2.数组实现 3.Predicate校验链2.1.使用Predicate改写代码2.1.更丰富的条件拓展 4.总结 1. 概述 1.1.背景 在最近的开发中遇到了这么一个需求&#xff0c;需要对业务流程中的各个参数做前置校验…

社科院与杜兰大学能源管理硕士——环境不会改变,解决之道在于改变自己

随着社会经济的不断发展&#xff0c;职场竞争也愈发激烈、工作要求不断提高&#xff0c;许多从业人员既不想放弃工作&#xff0c;又想提升专业能力&#xff0c;深化对专业知识的理解&#xff0c;获取优质的证书。那么考研便是一个不错的方式。考研的专业有很多&#xff0c;我们…

dubbo之整合SpringBoot

目录 zookeeper安装 1.拉取ZooKeeper镜像 2.新建文件夹 3.挂载本地文件夹并启动服务 4.查看容器 5.进入容器&#xff08;zookeeper&#xff09; Dubbo Admin安装 1.下载dubbo-admin 2.zip包解压 3.修改配置文件 4.打包项目 5.启动jar 6.访问 构建项目 api模块 1.创建…