一文搞定SpringSecurity+vue前后端分离

2023-11-18

我好菜啊,学了好几天才明白一点点

前言

  把v部落git下来学一学,比起halo来说v部落会更加简单好懂一点。我看他用了SpringSecurity来做登录验证,那第一步就是学学这个SpringSecurity。
  然后我就发现了,我真的是太菜了,看博客,看视频都不尽如意。尤其是用vue配合SpringSecurity的情况下实在是费劲,看了好多资料感觉都不是我需要的,懂得人感觉特别简单,不懂的就很费劲,说来说去还是我太菜了。
  我也是看了好几天资料并结合v部落的代码,才算是琢磨出来一点点门道,所以就记录一下我这个学习的成果。主要是vue配合SpringSecurity来使用,双方互相用json传递数据。学习之前需要懂得以下技术:

  • Springboot
  • MyBatis
  • Vue,Axios,vue-router
  • ······
  • 其他零零散散的我就不说了

开发准备

  首先我们需要有以下两三个页面:

  • 登录页面,
  • 主页
    • 用户管理页面==>>管理员身份才能访问
    • 文章管理页面==>>普通用户才能访问

  这几个页面我是用vue写的,大家有时间也可以自己写写,当然部分代码我也是参考别人的,虽然有那么一点点缺陷,但不影响使用。我把页面放在我的码云上面,不想写的话可以git下来。
码云地址:https://gitee.com/siumu/blog_code.git
界面长这个样子:

在这里插入图片描述
在这里插入图片描述


后端开发

创建项目

  接下来就是准备后端的代码了,先创建一个项目,再建立一个数据库。刚开始自然是创建项目,在pom文件里把Spring Boot,SpringSecurity,MyBatis等等一些东西,以下就是我的pom依赖:

    <dependencies>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <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>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 使用undertow替换tomcat -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- hutool开源JSON工具 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-json</artifactId>
            <version>5.2.4</version>
        </dependency>
    </dependencies>

这里面有的是自带的,有的是我后来加上去的。也没啥复杂的东西。

建立数据库

  既然是用户角色权限控制,那自然就需要有一个用户表,一个角色表。根据v部落设计的数据库,一个用户可以有多个角色,所以用户与角色之间就是多对多的关系,那么就需要一个用户角色关系表。
  总结一下就是需要三张表分别是用户表、角色表、用户角色关系表。
在这里插入图片描述
  数据库我也放在码云上,直接导入sql文件即可

创建实体类

  接下来自然是建立实体类,跟数据库一一对应,这里也不复杂介绍,反正用的是v部落的数据库,看看字段注释就知道啥意思了。
  首先是用户实体类

@Data
public class User implements UserDetails {
    private Long id;             //主键
    private String username;     //用户名
    private String password;     //密码
    private String nickname;     //昵称
    private boolean enabled;     //是否禁用
    private List<Role> roles;    //用户角色
    private String email;        //邮箱
    private String userface;     //头像
    private Timestamp regTime;   //注册时间
    
    @Override
    public List<GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
        }
        return authorities;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

}

  为什么要实现UserDetails这个接口呢?如果了解过这方面的知识就会知道,使用SpringSecurity从数据库里拿用户信息需要实现这个接口,这个接口,提供了一系列的方法,比如账户是否过期啊,用户是否被锁定啊等等。
  其中有些字段我们数据库里有,比如用户名,密码,是否禁用之类的。但是有些就没有,所以我们需要重写这些方法,然后让他们统统返回true。如果不了解可以百度一下这方面的博客看一看,我们现在直接用它就行了。
  然后是角色实体类

@Data
public class Role {
    /**
     * 主键
     */
    private Long id;

    /**
     * 角色名称
     */
    private String name;

}

实体类就这么简单的完成了。
接下来就是配置数据库连接了,这应该简单,我就直接把application.yml放上来吧。

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/dbgirl?characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
server:
  port: 8080
创建mapper

  接下来就是写操作数据库部分了,大家熟悉什么就用什么,我是看MyBatis比较火,所以我也用MyBatis。
  以下是mapper层的接口与xml:

public interface UserMapper {
    /**
     * 根据用户名查询用户信息
     * @param username 用户名
     * @return
     */
    User selectByUserName(String username);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiumu.securitydemo.mapper.UserMapper">
    <resultMap id="UserAndRole" type="com.xiumu.securitydemo.model.pojo.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="nickname" property="nickname"/>
        <result column="password" property="password"/>
        <result column="enabled" property="enabled"/>
        <result column="email" property="email"/>
        <result column="userface" property="userface"/>
        <result column="regTime" property="regTime"/>
        <collection property="roles" ofType="com.xiumu.securitydemo.model.pojo.Role">
            <id column="rid" property="id"/>
            <result column="name" property="name"/>
        </collection>
    </resultMap>
    <sql id="UserAndRole">
        u.*,r.id rid,r.name
    </sql>
    <select id="selectByUserName" parameterType="string" resultMap="UserAndRole">
        select <include refid="UserAndRole"/>
        FROM user u 
        LEFT JOIN roles_user ru ON u.id = ru.uid 
        LEFT JOIN roles r ON ru.rid = r.id
        WHERE u.username = #{username}
    </select>
</mapper>

  多表连接查询就不细说了,大家肯定能看懂。无非就是根据用户名查询一条用户记录。

创建service

  接下来就是业务层。现在不都是说要面向接口编程嘛,那咱就先建立一个接口,继承UserDetailsService,为什么继承这个接口呢,了解过的话就会知道,SpringSecurity就是用这个接口的loadUserByUsername方法来从数据库获取信息,写个类实现这个接口重写方法就完事。

public interface UserService extends UserDetailsService {
}

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        return userMapper.selectByUserName(s);
    }
}
创建controller

  接下来就是创建控制层了,这个也是简单写两个就行了,就是处理文章管理和用户管理的两个请求。当然在此之前我们还是要写一个通用类,用来当做返回值。

@Data
public class ResultJSON {
    /**
     * 返回的状态码
     */
    private Integer code;

    /**
     * 返回信息
     */
    private String msg;


    /**
     * 返回的数据
     */
    private Object result;

    public ResultJSON() {
    }

    /**
     * 只返回状态码
     *
     * @param code 状态码
     */
    public ResultJSON(Integer code) {
        this.code = code;
    }

    /**
     * 不返回数据的构造方法
     *
     * @param code 状态码
     * @param msg  信息
     */
    public ResultJSON(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    /**
     * 返回数据的构造方法
     *
     * @param code   状态码
     * @param msg    信息
     * @param result 数据
     */
    public ResultJSON(Integer code, String msg, Object result) {
        this.code = code;
        this.msg = msg;
        this.result = result;
    }

    /**
     * 返回状态码和数据
     *
     * @param code   状态码
     * @param result 数据
     */
    public ResultJSON(Integer code, Object result) {
        this.code = code;
        this.result = result;
    }
}

  接下来开始写正式的controller。

@RestController
public class UserController {

    @GetMapping("/hello")
    public ResultJSON hello(){
        return new ResultJSON(2000,"hello blog!");
    }

    @GetMapping("/vip")
    public ResultJSON vip(){
        return new ResultJSON(2000,"hello 超级管理员!");
    }

}

普通用户只能访问这个/hello请求,管理员才能访问这个/vip请求。

SpringSecurity配置

  接下来就是重点内容了,如何让SpringSecurity只跟vue返回json数据。而且除了这些,前后端分离也有很多问题,比如跨域问题啊之类的。这些都需要考虑。
  对于跨域,我就很暴力了,直接统统允许就完事。且看如下代码:

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET","POST")
                .maxAge(3600);
    }
}

  跨域问题解决了,接下来就是SpringSecurity的配置了。我们可以慢慢来,一步步的配置。
  首先就是新建一个配置类,继承WebSecurityConfigurerAdapter类。重写以下两个方法。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/hello").hasRole("普通用户")
                .antMatchers("/vip").hasRole("超级管理员")
                .anyRequest().authenticated()
                .and().cors().and().csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }
}

我们看到这有两个方法,有着不同的作用,根据我这几天的半吊子理解,简单的说说

  • configure(HttpSecurity http),这个方法的主要作用就是设置某个请求需要什么权限才能访问,例如这里我设置/hello由普通用户这个角色访问,/vip由超级管理员这个角色访问。当然这只是基本的作用,还有很多其他的作用,比如跨域登录请求啊等等。
  • configure(AuthenticationManagerBuilder auth),这个方法的主要作用就是设置用户的权限,从数据库获取用户信息之类的。比如这里,我将userService传给它,它就能自动的从数据库获取用户的信息。

到这里并没有结束,接下来我们需要将这些配置一一完善。

密码加密配置

  新版本里不仅要配置用户的userService,还要配置passwordEncoder,也就是密码的加密解密。既然如此我们就实现一个PasswordEncoder接口好了。当然这也是我参考人家v部落的。代码如下。

public class Md5PasswordEncoderImpl implements PasswordEncoder {
    @Override
    public String encode(CharSequence charSequence) {
        return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
    }

    /**
     * @param charSequence 明文
     * @param s 密文
     * @return
     */
    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return s.equals(DigestUtils.md5DigestAsHex(charSequence.toString().getBytes()));
    }
}

然后把这个实现类配置到上面说的那个configure(AuthenticationManagerBuilder auth)方法里。

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userService)
            .passwordEncoder(new Md5PasswordEncoderImpl());
}

到这里并没有结束。

登录请求配置

  接下来就是配置登录请求的url,并允许不登录访问,我就闹过一次笑话,测试的时候没有开启这个不登录访问,结果登录测试居然给我返回我没有登录。
  也是一行代码就搞定的事情,在上面说的这个configure(HttpSecurity http)方法里。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/hello").hasRole("普通用户")
            .antMatchers("/vip").hasRole("超级管理员")
            .anyRequest().authenticated()
            .and().cors().and().csrf().disable()
            .formLogin().loginProcessingUrl("/login").permitAll();
}

到这里还没有结束。
  这个登录请求url配置完了,那我们登录的时候只需要访问这个url就行了。但是现在还是有问题,就是登录失败会直接给你返回一个登录页面,并不能给你返回一个json数据,我们想要的是它登录失败或者登录成功都应该是一个json数据返回给前端。

设置登录成功或失败返回JSON数据

  这个问题人家自然是考虑到了,所以能够设置登录成功处理与登录失败处理。我们只需要实现人家的登录认证成功接口与登录认证失败接口,再将其配置进去就可以了。
  那么我们就需要再写几个类,首先是封装一下登录失败或者登录成功的返回信息,当然我们把没有登录啊,没有权限啊,注销登录的返回信息都封装一下。

public class ResultArgsUtil {
    //登录验证失败
    public static String USER_NOT_EXIST_FAILURE_MSG = "账号或者密码错误";
    public static Integer USER_NOT_EXIST_FAILURE_CODE = 1004;

    //没有登录
    public static String USER_NOT_LOGIN_FAILURE_MSG = "未登录";
    public static Integer USER_NOT_LOGIN_FAILURE_CODE = 1002;

    //登录成功
    public static String USER_LOGIN_SUCCESS_MSG = "登录成功";
    public static Integer USER_LOGIN_SUCCESS_CODE = 1000;

    //无权限
    public static String AUTHORIZE_FAILURE_MSG = "没有权限";
    public static Integer AUTHORIZE_FAILURE_CODE = 1003;

    //注销成功
    public static String LOGOUT_SUCCESS_MSG = "注销成功";
    public static Integer LOGOUT_SUCCESS_CODE = 1005;

}

  我们既然要返回json数据,那肯定要设置响应头,把返回对象转成json数据返回给前端。这几个步骤是重复的,唯一不重复的就是返回的对象不一样,所以我们就再封装一个工具类,就是返回json数据的通用方法。

public class SecurityHandlerUtil {
    /**
     * security处理返回结果
     * @param response 响应
     * @param result 结果
     * @throws IOException
     */
    public static void responseHandler(HttpServletResponse response, ResultJSON result) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write(JSONUtil.toJsonStr(result));
        writer.flush();
        writer.close();
    }
}

接下来就是正式写登录认证成功或失败的接口实现类。

public class LoginSuccessHandlerImpl implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        SecurityHandlerUtil.responseHandler(httpServletResponse,new ResultJSON(USER_LOGIN_SUCCESS_CODE,USER_LOGIN_SUCCESS_MSG));
    }
}
public class LoginFailureHandlerImpl implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        SecurityHandlerUtil.responseHandler(httpServletResponse,new ResultJSON(USER_NOT_EXIST_FAILURE_CODE,USER_NOT_EXIST_FAILURE_MSG));
    }
}

然后再将这两个类配置到上面configure(HttpSecurity http)这个方法里。

 @Override
 protected void configure(HttpSecurity http) throws Exception {
     http.authorizeRequests()
             .antMatchers("/hello").hasRole("普通用户")
             .antMatchers("/vip").hasRole("超级管理员")
             .anyRequest().authenticated()
             .and().cors().and().csrf().disable()
             .formLogin().loginProcessingUrl("/login").permitAll()
             .successHandler(new LoginSuccessHandlerImpl())
             .failureHandler(new LoginFailureHandlerImpl());
 }

这当然还没完。

无权限,注销,未登录

  除了上面那个登录成功与失败处理,这些没有权限啊,未登录啊,注销啊,之类的都需要我们自己来配置一下。所以接下来我们就写一写这些情况下的处理类,当然每个类都要实现人家的接口。
  首先是注销的处理类。

public class LogoutHandlerImpl implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        SecurityHandlerUtil.responseHandler(httpServletResponse,new ResultJSON(LOGOUT_SUCCESS_CODE,LOGOUT_SUCCESS_MSG));
    }
}

  然后是没有权限的处理类。

public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        SecurityHandlerUtil.responseHandler(httpServletResponse,new ResultJSON(AUTHORIZE_FAILURE_CODE,AUTHORIZE_FAILURE_MSG));
    }
}

  接下来就是没有登录的处理类。

public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        SecurityHandlerUtil.responseHandler(httpServletResponse,new ResultJSON(USER_NOT_LOGIN_FAILURE_CODE,USER_NOT_LOGIN_FAILURE_MSG));
    }
}

  然后我们把这些实现类都配置到上面configure(HttpSecurity http)这个方法里。如此才是全部的配置。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/hello").hasRole("普通用户")
                .antMatchers("/vip").hasRole("超级管理员")
                .anyRequest().authenticated()
                .and().cors().and().csrf().disable()
                .formLogin().loginProcessingUrl("/login").permitAll()
                .successHandler(new LoginSuccessHandlerImpl())
                .failureHandler(new LoginFailureHandlerImpl())
                .and()
                .logout().logoutSuccessHandler(new LogoutHandlerImpl()).permitAll()
                .and()
                .exceptionHandling()
                .accessDeniedHandler(new AccessDeniedHandlerImpl())
                .authenticationEntryPoint(new AuthenticationEntryPointImpl());
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService)
                .passwordEncoder(new Md5PasswordEncoderImpl());
    }
}

可能遇到的问题

  • 跨域问题
  • vue每次请求的JSESSIONID不一致问题。这个问题也比较好解决就是在axios中设置withCredentials属性为true就可以。 在这里插入图片描述

项目效果

以下是项目运行的效果图。在这里插入图片描述
前后端代码都会放在码云上。所有用户的密码都是123

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文搞定SpringSecurity+vue前后端分离 的相关文章

随机推荐

  • 电信客户流失预测----科大讯飞xDataWhale

    记录第一次参加正式的数据挖掘竞赛 由科大讯飞xDatawhale举办的 电信客户流失预测挑战赛 报名链接 2022 iFLYTEK A I 开发者大赛 讯飞开放平台 一 赛题概要 赛题背景 随着市场饱和度的上升 电信运营商的竞争也越来越激烈
  • Day 12: Twin Transformer by 美团

    这是美团和澳大利亚阿德莱德大学联合发表的新文章 也是和 Transformer 相关的 以下是一些要点 Swin Transformer 的 Shifted Windows 虽然有效 但是由于尺寸不同 因此在用现有的深度学习模型来实现的时候
  • CentOS常用zip压缩和解压缩命令

    1 压缩文件夹为zip文件 root cgls zip r mydata zip mydata 2 把mydata zip解压到mydatabak目录里面 root cgls unzip mydata zip d mydatabak 3 m
  • 电脑开机后,显示屏无信号怎么处理?

    转自 微点阅读 https www weidianyuedu com 随着使用电脑的用户越来越多 而使用的用户遇到的问题就越多了 而经常用电脑的同学大部分都遇到过电脑显示器无信号的情况吧 其实相比显示器没有任何显示而言 电脑显示器无信号的故
  • SQLServer如何统计每两小时的值

    把当前时间的 时分转为数字 select CONVERT FLOAT replace CONVERT VARCHAR 6 GETDATE 108 思路 select sum 数字 年月日 小时 2取整 from 表 group by 年月日
  • kafka学习笔记(一)简介

    这是对我找到的学习资料的整理 非手打 参考 https kafka apachecn org intro html https blog csdn net weixin 39468305 article details 106346280
  • Cannot forward after response has been committed问题解决及分析

    通过TOMCAT把系统启动 可以正常登陆门户 登陆进去选择子系统的时候点击登陆的时候 可是去又回到了登陆界面 如此反复就是不能够进入子系统 查看后台报的错误 Cannot forward after response has been co
  • 数据库密码忘记了怎么办

    修改数据库密码 方法1 用SET PASSWORD命令 首先登录MySQL 格式 mysql gt set password for 用户名 localhost password 新密码 例子 mysql gt set password f
  • 应急响应-账户排查

    用户信息排查 在服务器被入侵之后 攻击者可能会建立相关账户 方便进行远程控制 主要采用一下几种 直接建立一个新用户 有时候为了混淆视听 账户名称和系统常用名相似 激活一个系统中的默认用户 但是这个用户不经常使用 建立一个隐藏用 在windo
  • java-通过ip获取地址

    添加maven依赖
  • 关于ArcMap中打开ArcToolbox导致闪退的解决办法

    最近好久不用ArcGis的小编要用到ArcMap去发送一个GP服务 发现按照套路打开ArcMap点击ArcToolbox时 发生了ArcMap的闪退现象 几经周折终于解决了问题 希望也遇到这类问题的同学能够参考解决 而不是无脑的去重装软件
  • C# 实现ESC退出窗口的几种方法

    实现ESC退出窗口的几种方法 引言 方法一 同步按钮法 方法二 监听按键法 方法三 隐藏按钮法 最后 引言 我们通常用通过点击取消按键或者右上角的 X 盒子退出的方法来实现关闭当前Form窗体 但要使用按键ESC退出关闭窗口就显得更加高级了
  • 解决SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]的方案!!!!!

    目录 前提 一 安装maven helper插件 1 安装 2 安装成功 3 使用 二 去掉冲突的依赖包 1 前面已找到目标依赖 去pom文件内操作 2 去除 3 最后就可以了 前提 今天单元测试遇到了jar包冲突 SLF4J Class
  • 自己学驱动17——ARM工作模式和ARM9寄存器

    1 ARM体系CPU的7种工作模式 1 用户模式 usr ARM处理器正常的程序执行状态 2 快速中断模式 fiq 用于高速数据传输或通道处理 3 中断模式 irq 用于通用的中断处理 4 管理模式 svc 操作系统使用的保护模式 5 数据
  • 【Python】PyCharm中调用另一个文件的函数或类

    欢迎来到Python专栏 PyCharm中调用另一个文件的函数或类 o o 嗨 我是小夏与酒 博客主页 小夏与酒的博客 该系列文章专栏 Python学习专栏 文章作者技术和水平有限 如果文中出现错误 希望大家能指正 欢迎大家关注 目录 Py
  • 数据结构:栈

    文章目录 栈 一 概述 二 添加数据 三 删除数据 栈 一 概述 栈 Stack 是一种特殊的线性表 它只允许在一端进行插入和删除操作 通常被称为 后进先出 Last In First Out LIFO 的数据结构 栈由一系列元素组成 每个
  • python常见的三种格式化输出

    Author Father Teng Name input name Age int input age Job input job info info of 0 Name 0 Age 1 Job 2 format Name Age Job
  • 【源码改造】Flink-jdbc-connector源码简析+改造支持谓词下推

    一 需求背景分析 flink sql在维表关联时 会有一个场景 当右表的数据量比较大且有些数据虽然符合join条件 但其实对于下游来说数据可能没用 这样就浪费了flink的计算资源 且拉低了数据处理能力 如果在join前就把维表的数据进一步
  • rac术语小结

    author skatetime 2010 03 01 rac术语小结 CSS 集群同步服务 Cluster Syncronization Service 功能 Manages the cluster configuration by co
  • 一文搞定SpringSecurity+vue前后端分离

    我好菜啊 学了好几天才明白一点点 前言 把v部落git下来学一学 比起halo来说v部落会更加简单好懂一点 我看他用了SpringSecurity来做登录验证 那第一步就是学学这个SpringSecurity 然后我就发现了 我真的是太菜了