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

文章目录

    • 开头
    • 简介
    • 环境搭建
    • 入门使用
      • 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 被废弃了,不推荐使用。

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

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

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

相关文章

React路由 基本使用 嵌套路由 动态路由 获取路由参数 异步路由 根据配置文件来生成路由

文章目录 React-router的三个版本react-router使用嵌套路由动态路由 获取路由参数Params参数Query参数Location信息 控制跳转地址异步路由根据配置文件生成路由 React-router的三个版本 React-router 服务端渲染使用React-router-dom 浏览器端渲染使用React-router-native Rea…

API项目3:API签名认证

问题引入 我们为开发者提供了接口&#xff0c;却对调用者一无所知 假设我们的服务器只能允许 100 个人同时调用接口。如果有攻击者疯狂地请求这个接口&#xff0c;那是很危险的。一方面这可能会损害安全性&#xff0c;另一方面耗尽服务器性能&#xff0c;影响正常用户的使用。…

若依前后端分离版本el-select下拉框字典如何设置默认值。

在若依前后端分离框架中&#xff0c;如何给下拉框设置默认值&#xff0c;刚入门的小伙伴&#xff0c;可能会不知道如何去做。 本章教程&#xff0c;主要以用户管理模块中的添加用户举例说明如何设置用户性别默认值为男。 解决思路 首先&#xff0c;我们需要找到打开新增页面的方…

【工具】前端js数字金额转中文大写金额

【工具】前端js数字金额转中文大写金额 代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>金额转…

多功能点击器(文末附Gitee源码)——光遇自动弹奏

之前提到的多功能点击器&#xff0c;使用场景比较多&#xff0c;之前玩光遇喜欢在里面弹琴&#xff0c;想到用这个点击器也能自动弹琴&#xff0c;跟别的自动弹琴脚本不一样&#xff0c;这个比较简单容易操作。 借这个光遇自动弹琴使用教程再讲解一下这个多功能点击头的使用方法…

ModelMapper的常见用法 ,号称是beanUtils.copyProp....的升级版??,代码复制粘贴即可复现效果,so convenient

官网案例 以下将官网案例做一个解释 1&#xff09;快速入门 递归遍历源对象的属性拷贝给目标对象 拷贝对象下对象的属性值 Data class Order {private Customer customer;private Address billingAddress; }Data class Customer {private Name name; }Data class Name {pr…

在三维可视化项目中,B/S和C/S架构该如何选择?

一、什么是B/S和C/S 在3D数据可视化中&#xff0c;有两种常见的架构模式&#xff1a;BS&#xff08;Browser/Server&#xff09;和CS&#xff08;Client/Server&#xff09; B/S模式 B/S模式是指将3D数据可视化的逻辑和处理放在服务器端&#xff0c;而在客户端使用浏览器进行…

智能汽车智能网联

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c;多看一眼都是你的不…

rom定制系列------小米6x_MIUI14_安卓13刷机包修改写入以及功能定制 界面预览

在接待一些定制化系统中。有很多工作室或者一些特殊行业的友友需要在已有固件基础上简略修改其中的功能。方便使用。例如usb调试默认开启。usb安装设置以及usb安装与内置删减一些app的定制服务。今天给友友预览其中小米6X此款机型定制相关的一些界面与功能演示。 定制机型以及…

公司新来的00后测试开发,让我对“跨界”二字有了全新认识

最近&#xff0c;我们部门迎来了一位新面孔——一个00后的年轻人&#xff0c;阿沅。初见他时&#xff0c;我以为他只是众多新入职员工中的普通一员&#xff0c;毕竟他的专业背景与我们的IT行业似乎相去甚远——广告学。然而&#xff0c;他的到来&#xff0c;却如同一阵清风&…

IDEA中的Postfix Completion与Live Templates功能详解

目录 前言1. Postfix Completion&#xff08;后缀补全&#xff09;1.1 什么是Postfix Completion1.2 使用Postfix Completion的步骤1.3 Postfix Completion的具体应用1.4 自定义Postfix Completion 2. Live Templates&#xff08;实时模板&#xff09;2.1 什么是Live Templates…

聊聊 Facebook Audience Network 绑定收款账号的问题

大家好&#xff0c;我是牢鹅&#xff01;本篇是Facebook开发者系列的第五篇&#xff0c;最近看见好多群友在群里问这个&#xff0c;说Facebook的变现账户在绑定国内的银行账户时&#xff08;有些用户反馈就算不是国内的卡也会出现该问题&#xff09;&#xff0c;显示“无法绑定…

【WRF工具】QGis插件GIS4WRF:根据嵌套网格生成namelist.wps文件

【WRF工具】QGis插件GIS4WRF:根据嵌套网格生成namelist.wps文件 准备:WRF嵌套网格QGis根据嵌套网格生成namelist.wps文件检查:根据namelist.wps绘制模拟区域参考GIS4WRF 是一个免费且开源的 QGIS 插件,旨在帮助研究人员和从业者进行高级研究天气研究与预报(WRF)模型的建模…

利用可解释性技术增强制造质量预测模型

概述 论文地址&#xff1a;https://arxiv.org/abs/2403.18731 本研究提出了一种利用可解释性技术提高机器学习&#xff08;ML&#xff09;模型性能的方法。该方法已用于铣削质量预测&#xff0c;这一过程首先训练 ML 模型&#xff0c;然后使用可解释性技术识别不需要的特征并去…

Lucene 倒排索引

倒排索引是什么&#xff1f; 【定义】倒排索引&#xff08;Inverted Index&#xff09;是一种用于信息检索的数据结构&#xff0c;尤其适用于文本搜索。它与传统索引的主要区别在于&#xff0c;传统索引是根据文档来查找词语的位置&#xff0c;而倒排索引则是根据词语来查找文…

vmware虚拟机 报错:客户机操作系统已禁用 CPU,请关闭或重置虚拟机 的解决方法

打开cpu虚拟化全部进行勾选 ctrl e 进行关机 勾选上打开就好了 如果没有那个选项 关机>打开虚拟机>管理>更改硬件兼容性> 往小处改改> >更改此虚拟机

MySQL连接查询:联合查询

先看我的表结构 emp表 联合查询的关键字&#xff08;union all, union&#xff09; 联合查询 基本语法 select 字段列表 表A union all select 字段列表 表B 例子&#xff1a;将薪资低于5000的员工&#xff0c; 和 年龄大于50 岁的员工全部查询出来 第一种 select * fr…

x-file-storage:一款强大的文件聚合存储解决方案

嗨&#xff0c;大家好&#xff0c;我是小华同学&#xff0c;关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法 dromara/x-file-storage 是一个由 Dromara 社区开发和维护的开源项目&#xff0c;旨在提供一个高效、可靠的文件存储解决方案。该项目以其强大的功能和…

正则表达式-“三剑客”(grep、sed、awk)

1.3正则表达式 正则表达式描述了一种字符串匹配的模式&#xff0c;可以用来检查一个串是否含有某种子串&#xff0c;将匹配的子串替换或者从某个串中取出符号某个条件的子串等&#xff0c;在linux中代表自定义的模式模版&#xff0c;linux工具可以用正则表达式过滤文本。Linux…

新版 Notepad++ 下载与安装教程

一、软件准备&#xff1a;麻烦点我 二、双击下载好的 notepad 软件进行安装&#xff0c;选择 “简体中文”。 三、默认 “下一步” 安装。 四、单击 “我接受” 按钮。 五、自定义安装位置&#xff0c;个人建议安装在 D 盘。 六、选择组件&#xff0c;默认 “下一步”。 七、勾…