spring |Spring Security安全框架 —— 认证流程实现

news2025/1/11 4:49:09

文章目录

    • 开头
    • 简介
    • 环境搭建
    • 入门使用
      • 1、认证
        • 1、实体类
        • 2、Controller层
        • 3、Service层
          • 3.1、接口
          • 3.2、实现类
          • 3.3、实现类:UserDetailsServiceImpl
        • 4、Mapper层
        • 3、自定义token认证filter
    • 注意事项
    • 小结

开头

  • Spring Security 官方网址:Spring Security官网

开头贴官网,有事找官方!

在这里插入图片描述

简介

介绍的话不多说,就一句:

  • Spring Security 是一个安全管理框架。

    • 一般用于中大型项目。小项目使用shiro,shiro上手简单。
    • 不过,一般是这样。小项目练手用也是相当可以的。

好吧!这是三句话,没跑。

环境搭建

基于SpringBoot3搭建的项目,妥妥好用。

组件SpringBoot2.XSpringBoot3.X
JDKJDK 8、9JDK 17+
JPAJPA2.0+JPA3.0+
ServletServlet 3.1+Servlet 5.0
SpringSpring Framework 5+Spring Framework 6+
GradleGradle 4.xGradle7.3

1、依赖导入

	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.4</version>
    </parent>

<!--        spring web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
<!--        spring Security-->
	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
    <!--        mybatis-plus-->
     <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
         <version>3.5.7</version>
     </dependency>
     
     <!--    mysql驱动-->
     <dependency>
         <groupId>com.mysql</groupId>
         <artifactId>mysql-connector-j</artifactId>
     </dependency>

2、配置文件

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/dataBase?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
# mybatisPlus 配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    # 日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      #      逻辑删除
      logic-delete-field: delFag
      logic-delete-value: 1
      logic-not-delete-value: 0
      #      主键自增
      id-type: auto

入门使用

1、认证

废话不多说,贴个认证流程图:

在这里插入图片描述

1、SpringSecurity Config类


//整点实在的,一整套流程,绝对全面。
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private AuthenticationTokenFilter tokenFilter;

    @Autowired
    private AuthenticationEntryPointImpl authenticationEntryPoint;
	/*
		PasswordEncoder是一个用于密码加密的接口,它封装了多种主流的加密方法,
		它们用于密码的安全存储和校验。但计算开销也相对较大,
		因此在面对高并发性能要求的大型信息系统时:
			推荐使用会话、OAuth、Token等短期加密策略来实现系统的信息安全。
	*/
    @Bean
    public PasswordEncoder getPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
	/*
		负责注册为应用程序提供认证服务的 。
	*/
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config ) throws Exception {
        return config.getAuthenticationManager();
    }
	/*
		Security Filter是通过FilterChainProxy而不是DelegatingFilterProxy注册进SecurityFilterChain的。	
		过滤器链,配置
	*/
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        http.csrf(csrf -> csrf.disable()); //关闭csrf防护

        http.authorizeHttpRequests(auth ->
                auth
                        .requestMatchers("/user/login")//请求路径匹配
                        .permitAll()//放行,不作拦截
                        .anyRequest()//其他请求
                        .authenticated());//认证


        //        自定义token 认证,用于登录后续请求放行。通过springSecurity上下文判断
        http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

1、实体类
//主要就是用:username和 password
@Data
public class SysUser implements Serializable {
    private static final long serialVersionUID = 662137028719131857L;

    private Long id;
/**
     * 用户名
     */
    private String username;
/**
     * 昵称
     */
    private String nickname;
/**
     * 密码
     */
    private String password;
/**
     * 用户类型:0:普通 1:管理员
     */
    private String type;
/**
     * 账号状态:0:正常 1:停用
     */
    private String status;
/**
     * 邮箱
     */
    private String email;
/**
     * 手机号
     */
    private String phoneNumber;
/**
     * 用户性别(0:男 1:女 2 :匿名)
     */
    private String sex;
/**
     * 头像
     */
    private String avatar;

    private Integer isDelete;




}
2、Controller层
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private SysUserService userService;

    @PostMapping("/login")
    public R login(@RequestBody SysUser user){


        return userService.login(user);
    }
}

3、Service层
3.1、接口
public interface SysUserService extends IService<SysUser> {


    /**
     * 登录
     * @param user
     * @return
     */
    R login(SysUser user);
}

3.2、实现类
@Service
public class SysUserServiceImpl extends ServiceImpl<UserMapper, SysUser> implements SysUserService {


    @Autowired
    private AuthenticationManager authenticationManager;
    @Override
    public R login(SysUser user) {

        //重写userDetailsService 处理
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

        if(Objects.isNull(authenticate)){
            throw new AppException(AppExceptionMsgEnum.LOGIN_ERROR);
        }
		
		//jwt令牌信息
		//……
		
        LoginUser loginuser = (LoginUser) authenticate.getPrincipal();


		//自定义返回
        return  R.success(loginuser.getUser());
    }
}
3.3、实现类:UserDetailsServiceImpl
  • 用于用户信息认证处理
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getUsername,username);

        SysUser userResult = userMapper.selectOne(queryWrapper);

        if(Objects.isNull(userResult)){
            throw new AppException(AppExceptionMsgEnum.LOGIN_ERROR);
        }




        return new LoginUser(userResult);
    }
}
4、Mapper层
public interface UserMapper extends BaseMapper<SysUser> {
}

3、自定义token认证filter

@Component
public class AuthenticationTokenFilter extends OncePerRequestFilter {


    @Autowired
    private UserMapper userMapper;

    @Override
    protected void doFilterInternal(HttpServletRequest request,  HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, ServletException, IOException {
        //获取token
        String token = request.getHeader("token");

        String uri = request.getRequestURI();
        if(uri.equals("/gate/logout")){
            filterChain.doFilter(request,response);
            return;
        }

        //放行未携带token 的请求,交由后面拦截器处理
        if (token == null) {
            filterChain.doFilter(request,response);
            return;
        }

		//获取用户信息,这里由于没有使用jwt,所以仿制了一下
        SysUser user = new SysUser();
        user.setUsername(token);
        LoginUser loginUser = new LoginUser();
        loginUser.setUser(user);

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null,null);

        //存储到SecurityContextHolder
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        //放行
        filterChain.doFilter(request,response);


    }

}

注意事项

  1. 认证失败后,会抛出异常。使用全局异常处理器 + 自定义异常 使用。
  • 自定义返回类
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)// 属性为NULL 不序列化
public class R<T> {

    private int status;
    //    private boolean success;
    private String message;
    private T data;

    @JSONField(format="yyyy-MM-dd HH:mm:ss")
    private DateTime time;

    /**
     * 正确result
     * @param data
     * @param <T>
     * @return
     */
    public static <T> R<T> success(T data){
        R<T> r = new R<>();
//        r.success = true;
        r.status = 200;
        r.data = data;
        r.message = "successful";
        r.time = DateTime.now();

        return r;
    }

    public static <T> R<T> success(String msg ,T data){
        R<T> r = new R<>();
//        r.success = true;
        r.status = 200;
        r.data = data;
        r.message = msg;
        r.time = DateTime.now();

        return r;
    }

    /**
     * 错误result
     * @param msg
     * @param status
     * @param <T>
     * @return
     */
    public static <T> R<T> error(String msg,int status){
        R<T> r = new R<>();
//        r.success = false;
        r.status = status;
        r.message = msg;
        r.time = DateTime.now();

        return r;
    }

    /**
     * 错误result
     * @param msg
     * @param <T>
     * @return
     */
    public static <T> R<T> error(String msg){
        R<T> r = new R<>();
//        r.success = false;
        r.message = msg;
        r.time = DateTime.now();

        return r;
    }

    public static <T> R<T> error(AppExceptionMsgEnum appExceptionMsg){
        R<T> r = new R<>();
//        r.success = false;
        r.status = appExceptionMsg.getStatus();
        r.message = appExceptionMsg.getMsg();
        r.time = DateTime.now();

        return r;
    }


}
  • 全局异常处理器
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {


    @ExceptionHandler(value = BadCredentialsException.class)
    public static <T> R<T> badCredentialsException(BadCredentialsException e) throws InterruptedException {
        return R.error(AppExceptionMsgEnum.LOGIN_ERROR);
    }


    @ExceptionHandler(value = Exception.class)
    public static <T> R<T> exceptionHandle(Exception e) throws InterruptedException {
        if(e instanceof AppException){
            AppException app = (AppException) e;
            return R.error(app.getMsg(),app.getStatus());
        }

        return R.error(e.toString(),500);
    }
}

  • 自定义异常
@EqualsAndHashCode(callSuper = true)
@Data
public class AppException extends RuntimeException{
    private int status;

    private String msg;

    public AppException(AppExceptionMsgEnum appExceptionMsgEnum) {
        this.status = appExceptionMsgEnum.getStatus();
        this.msg = appExceptionMsgEnum.getMsg();
    }
}

  • 异常枚举类
public enum AppExceptionMsgEnum {

    //成功
    SUCCESS(200,"操作成功"),
    //失败
    NEED_LOGIN(401,"需要登录后操作"),
    NO_OPERATION_AUTH(403,"无权限操作"),
    USERNAME_EXIST(501,"用户名已存在"),
    PHONE_NUMBER_EXITS(502,"手机号已存在"),
    EMAIL_EXIST(503,"邮箱已存在"),
    REQUIRED_USERNAME(504,"必须填写用户名"),
    LOGIN_ERROR(505," 用户名或密码错误"),
    WRONG_PAGE_PARAM(506,"wrong page param"),
    CONTENT_NOT_NULL(507,"评论内容不能为空"),
    FILENAME_ERROR(508,"上传文件错误"),
    ONLY_UPDATE_SELF(509,"只能修改自己的信息"),
    SERVER_ERROR(500,"SERVER ERROR")
    ;
    private int status;
    private String msg;

    AppExceptionMsgEnum(int status, String msg) {
        this.status = status;
        this.msg = msg;
    }

    public int getStatus() {
        return status;
    }

    public String getMsg() {
        return msg;
    }
}

例如:

  • API
  • 支持模型类型

小结

  • SpringSecurity5.7.0之前

    • 常见的 Spring HTTP Security 配置类都会继承一个 WebSecurityConfigureAdapter 类。
  • 从 5.7.0-M2 起,WebSecurityConfigureAdapter 被废弃了,不推荐使用。

    • 组件化开始,更加灵活。
  • 基础认证功能完成。完结撒花!!
    在这里插入图片描述

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

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

相关文章

leetcode 1027 最长等差数列 题目的思考

https://leetcode.cn/problems/longest-arithmetic-subsequence/ 如果序列是&#xff1a;3 0 3&#xff0c;枚举的公差是3 对于第一个数3&#xff0c;它的序列长度就是他自己3 对于第二个数0&#xff0c;它的序列长度就行它自己0 对于第三个数&#xff0c;它的序列长度应该是【…

【未知列名注入】

简介 在sql注入中&#xff0c;如果服务器过滤了column_name阻止我们获取列名&#xff0c;我们该如何绕过 一、union 绕过 使用union构造多个表&#xff0c;把数据表和构造的123表连接起来&#xff0c;我们看一下构造过程: 查询user表数据 select * from user;Union联合查询…

基于协同过滤的景区旅游可视化与景区推荐系统(自动爬虫,地点可换)

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍过程展示项目移植每文一语 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍 本项目是一个综合性的旅游景区数据管理与分析推荐系统,集成了用…

Qt:设置程序图标与主窗口背景图片

目录 设置程序图标&#xff1a; 设置主窗口背景图片&#xff1a; 设置程序图标&#xff1a; 在设置图标之前先准备一张ico图标&#xff0c;没有ico图标的可以准备一张图片&#xff0c;然后找一个在线的ico转换网站去转换一张ico文件出来。 然后打开项目文件所在的文件夹&am…

C语言 | Leetcode C语言题解之第467题环绕字符串中唯一的子字符串

题目&#xff1a; 题解&#xff1a; #define MAX(a, b) ((a) > (b) ? (a) : (b))int findSubstringInWraproundString(char * p) {int dp[26];int len strlen(p);memset(dp, 0, sizeof(dp));int k 0;for (int i 0; i < len; i) {if (i && (p[i] - p[i - 1] …

Spark高级用法-数据源的读取与写入

目录 数据读取 数据写入 总结 数据读取 读文件 read.json read.csv csv文件有两个部分构成 头部数据&#xff0c;也就是字段数据&#xff0c;行数数据 read.orc 读数据库 read.jdbc(jdbc连接地址,table表名,properties{user用户名,password密码,driver驱动信息}) 缺少连…

机器学习基础概念(3)

小小考一下大家前两节的内容(坏笑) 我们如何评判一个机器学习模型的性能呢&#xff1f; 通常是判断它的泛化能力&#xff08;对于未知数据的处理能力&#xff09; 那么对于泛化能力是否有一个标准&#xff0c;比如在未知的1万个数据中&#xff0c;泛化能力 模型一90% >…

【分布式事务-02】分布式事务seata的安装下载与环境搭建

redis系列整体栏目 内容链接地址【一】分布式事务之2pc两阶段提交https://zhenghuisheng.blog.csdn.net/article/details/142406325【一】分布式事务seata的安装下载与环境搭建https://zhenghuisheng.blog.csdn.net/article/details/142893117 分布式事务seata的安装下载与环境…

java服务器技术

1. Java EE&#xff08;Java Enterprise Edition&#xff09; Java EE是一套为企业级应用提供的完整解决方案&#xff0c;它包括了Java Servlet、JSP&#xff08;JavaServer Pages&#xff09;、EJB&#xff08;Enterprise JavaBeans&#xff09;、JPA&#xff08;Java Persist…

【风力发电】基于模糊逻辑控制的风电系统MPPT

摘要 本文基于模糊逻辑控制 (Fuzzy Logic Control, FLC) 实现了风力发电系统的最大功率点追踪 (MPPT)。FLC 由于其不依赖于精确数学模型的特点&#xff0c;能够有效应对风速变化导致的非线性和不确定性问题。通过对风速和功率的模糊化处理&#xff0c;该方法提高了风电系统的功…

ros1:使用C++编写ros程序,获取IMU数据,使用gazebo仿真

cd catkin_ws/src/catkin_create_pkg imu_pkg roscpp rospy sensor_msgs在src目录下创建&#xff0c;imu_node.cpp #include "ros/ros.h" #include "sensor_msgs/Imu.h" #include "tf/tf.h"void IMUCallback(sensor_msgs::Imu msg){if(msg.orien…

深兰科技|“武汉市AI心理热线医工交叉研发合作基地”正式揭牌

2024年10月10日是第33个世界精神卫生日&#xff0c;以“共建共治共享&#xff0c;同心健心安心“为主题的武汉市2024年世界精神卫生日主题活动暨第三届武汉青年心理情景剧展演闭幕式&#xff0c;在武汉隆重举行。期间&#xff0c;还举行了武汉市精神卫生中心与深兰科技(武汉)公…

Video-LLaMA部署

Video-LLaMA: An Instruction-tuned Audio-Visual Language Model for Video Understanding

计组_输入输出系统

2024.08.05&#xff1a;计算机组成原理输入输出学习笔记 第25节 输入输出系统 5.1 IO基本职能5.2 IO接口的通用结构5.3 IO数据传送控制方式5.3.1 程序直接控制&#xff08;程序查询控制&#xff09;&#xff08;1&#xff09;独占查询&#xff08;2&#xff09;定时查询 5.3.2 …

衡石分析平台---分析人员手册

分析人员是 HENGSHI SENSE 系统最主要的用户。在企业内部&#xff0c;他们应该是了解相关业务&#xff0c;需要在纷繁复杂的数据中发现数据规律的人。 从岗位上来说&#xff0c;他们可能来自运营部门&#xff0c;需要从日常运营数据中发现用户的使用规律&#xff1b;可能来自销…

【element-tiptap】如何引进系统中的字体?

源码地址&#xff1a; https://github.com/Leecason/element-tiptap 源码中给出的字体如下 可以看到&#xff0c;咱们日常需要的黑体、微软雅黑等都没有&#xff0c;所以这篇文章来探索一下怎么加字体。 另外呢&#xff0c;肯定有小伙伴发现&#xff0c;这个按钮点击的时候&am…

ssm资产管理信息系统+vue

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码请私聊我 需要定制请私聊 目录 摘要 I Abstract II 1 绪论 - 1 - 1.1 研究背景与意义 - 1 - 1.1.1 研究背景 - 1 - 1.1.2 研究意义 …

jQuery——区别onload和ready

区别&#xff1a;window.onload 与 $&#xff08;document&#xff09;. ready&#xff08;&#xff09; window.onload 包括页面的图片加载完后才会回调&#xff08;晚&#xff09;只能有一个监听回调 $&#xff08;document&#xff09;. ready&#xff08;&#xff09; 等同…

echarts图例右侧竖向排列

实操链接戳这里哈 left代表图例在水平放置的位置&#xff0c;有left、center、right top代表图例在垂直方向的位置&#xff0c;有top、middle、bottom width是最主要的&#xff0c;当设置的宽度比较小时&#xff0c;才会迫使图例换行&#xff0c;从而形成竖直排列的现像。 lege…

MySQL:小练习

安装MySQL并且创建库和表 一&#xff0c; windows采用zip安装MySQL1. 卸载已有的MySQL2. 下载zip格式3. 解压缩4. 配置环境变量5. 创建配置文件6. 初始化mysql服务 二&#xff0c; 新建产品库mydb6_product&#xff0c;新建三张表 一&#xff0c; windows采用zip安装MySQL 1. …