SpringSecurity学习(七)授权

授权

什么是权限管理
权限管理核心概念
SpringSecurity权限管理策略
基于URL地址的权限管理
基于方法的权限管理

一、权限管理

在这里插入图片描述

二、授权核心概念

在认证的过程成功之后会将当前用户登录信息保存到Authentication对象中,Authentication对象中有一个getAuthorities()方法,用来返回当前登录用户具备的权限信息。该方法返回值是Collections<extends GrantedAuthority>,当需要进行权限判断时,就根据集合返回权限信息调用对应方法进行判断。

public interface Authentication extends Principal, Serializable {
	Collection<? extends GrantedAuthority> getAuthorities();
    // 省略
}

那么针对返回值应该如何理解?是权限还是角色?
RBAC(Role/Resource Base Access Controll)
针对收取按可以是基于角色权限管理基于资源权限管理,从设计层面来说:角色和权限是俩个不同的东西。权限是一些具体的操作,角色是一些权限的集合。eg:READ_BOOK和ROLE_ADMIN是完全不同的。因此至于返回值是什么应当取决于业务的设计。

  • 基于角色权限设计:用户<=>角色<=>资源三者关系,返回就是用户的角色
  • 基于资源权限设计:用户<=>权限<=>资源三者关系,返回就是用户的权限
  • 基于角色和资源权限设计:用户<=>角色<=>权限<=>资源的关系,返回统称为用户的权限
    这里统称为权限,是因为代码层面来说权限和角色没有太大的不同都是权限。其在SpringSecurity中处理方式也基本相同。唯一的区别是会自动给角色多加一个ROLE_前缀。

三、两种权限管理策略

SpringSecurity主要提供俩种权限管理策略:
可以访问系统中的那些资源(URL、Method)

  1. 基于过滤器(URL)的权限管理(FilterSecurityInterceptor)
    基于过滤器的权限管理主要用来拦截HTTP请求,拦截下来后,根据http请求地址进行权限校验。
  2. 基于AOP(Method)的权限管理(MethodSecurityInterceptor)
    基于AOP权限管理主要用来处理方法级别的权限问题。当需要调用某一方法时,通过aop将操作拦截,然后判断用户是否具备相关权限。

1、基于URL权限管理

1.1 案例

编写HiController
@RestController
public class HiController {

    @RequestMapping("/")
    public String home() {
        return "<h1>HI SPRING SECURITY</hi>";
    }

    @RequestMapping("/admin")
    public String admin() {
        return "<h1>HI SPRING ADMIN</hi>";
    }

    @RequestMapping("/user")
    public String user() {
        return "<h1>HI USER</hi>";
    }

    @RequestMapping("/getInfo")
    public Authentication getInfo() {
        return SecurityContextHolder.getContext().getAuthentication();
    }
}
编写SecurityConfig
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("root").password("{noop}123").roles("ADMIN","USER").build());
        manager.createUser(User.withUsername("whx").password("{noop}123").roles("USER").build());
        manager.createUser(User.withUsername("dy").password("{noop}123").authorities("READ_INFO").build());
        return manager;
    }
    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(req -> {
            req.mvcMatchers("/admin").hasRole("ADMIN");
            req.mvcMatchers("/user").hasRole("USER");
            req.mvcMatchers("/getInfo").hasAuthority("READ_INFO");
            req.anyRequest().authenticated();
        });
        http.formLogin();
        http.csrf().disable();
        return http.build();
    }
}

1.2 权限表达式

public interface SecurityExpressionOperations {
	// 获取用户权限信息
	Authentication getAuthentication();
	// 当前用户是否具备指定权限
	boolean hasAuthority(String authority);
	// 当前用户是否具备指定权限中的任意一个
	boolean hasAnyAuthority(String... authorities);
	// 当前用户是否具备指定角色
	boolean hasRole(String role);
	// 当前用户是否具备指定角色任意一个
	boolean hasAnyRole(String... roles);
	// 放行所有请求
	boolean permitAll();
	// 拒绝所有请求
	boolean denyAll();
	// 当前用户是否匿名用户
	boolean isAnonymous();
	// 当前用户是否已经认证成功
	boolean isAuthenticated();
	// 当前用户是否通过RememberMe记住我自动登录
	boolean isRememberMe();
	// 当前用户是否既不是宁ing用户也不是通过rememberMe自动登录
	boolean isFullyAuthenticated();
	// 当前用户是否具备指定目标的指定权限信息
	boolean hasPermission(Object target, Object permission);
	// 当前用户是否具备指定目标的指定权限信息
	boolean hasPermission(Object targetId, String targetType, Object permission);
}

1.3 URL匹配规则:antMatchers、mvcMatchers、regexMatchers

antMatchers和mvcMatchers的区别,在于mvcMatchers更加强大通用,而regexMatchers的好处是支持正则表达式。
在这里插入图片描述

2. 基于方法的权限管理

基于方法的权限管理通过AOP来实现,SpringSecurity中通过MethodSecurityInterceptor来提供相关实现。不同在于FilterSecurityInterceptor只是在请求之前进行前置处理,MethodSecurityInterceptor除了前置处理之外还可以进行后置处理。前置处理就是在请求之前判断是否具有响应权限,而后置处理则是对方法执行结果进行二次过滤。前置处理和后置处理对应了不同的实现类。

@EnableGlobalMethodSecurity

@EnableGlobalMethodSecurity注解用来开启权限,用法如下:

@EnableWebSecurity
// 开启全局方法权限配置,仅可能的显示配置三个属性为true
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
public class SecurityConfig2 {
  • prePostEnabled :开启SpringSecurity提供的四个权限注解@PostAuthorize@PostFilter@PreAuthorize@PreFilter
  • securedEnabled:开启SpringSecurity提供的@Secured注解支持,该注解不支持权限表达式
  • jsr250Enabled:开启JSR-250提供的注解,主要是@DenyAll@PermitAll@RolesAll,同样的这些注解也不支持权限表达式。
注解含义
@PostAuthorize在目标方法执行之后进行权限校验
@PostFilter在目标方法执行之后对返回结果进行过滤
@PreAuthorize在目标方法执行之前进行权限校验
@PreFilter在目标方法执行之前对方法参数进行过滤
@Secured访问目标方法必须具备对应的角色
@DenyAll拒绝所有访问
@PermitAll允许所有访问
@RolesAll访问目标方法必须具备对应的角色

这些基于方法的权限管理相关的注解,由于后四个不常用,一般来说只需要设置prePostEnabled =true即可
权限表达式:例子hasRole("admin")

案例:

编写SecurityConfig
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
public class SecurityConfig2 {
    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("root").password("{noop}123").roles("ADMIN","USER").build());
        manager.createUser(User.withUsername("whx").password("{noop}123").roles("USER").build());
        manager.createUser(User.withUsername("dy").password("{noop}123").authorities("READ_INFO").build());
        return manager;
    }
    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(req -> {
            req.anyRequest().authenticated();
        });
        http.formLogin();
        http.csrf().disable();
        return http.build();
    }
}
编写T2Controller
@RestController
@RequestMapping("t2")
public class T2Controller {

    @PreAuthorize("hasRole('ADMIN')  and authentication.name == 'root'")
    @RequestMapping("/")
    public String home() {
        return "<h1>HI SPRING SECURITY</hi>";
    }

    // http://localhost:8888/t2/name?name=root
    @PreAuthorize("authentication.name == #name")
    @RequestMapping("/name")
    public String admin(String name) {
        return "<h1>HI SPRING " + name + "</hi>";
    }

    //    [ { "id":"1","username":"huathy" },
//    { "id":"2","username":"dy" } ]
    @PreFilter(value = "filterObject.id%2 != 0", filterTarget = "users")  //filterTarget参数必须是集合类型
    @RequestMapping("/add")
    public List<User> add(@RequestBody List<User> users) {
        List<User> result = new ArrayList<>();
        for (User user : users) {
            result.add(User.build(user.getId(), user.getUsername()));
        }
        return result;
    }

    // http://localhost:8888/t2/userId?id=1
    @PostAuthorize("returnObject.id == 1")
    @RequestMapping("/userId")
    public User userId(Integer id) {
        User user = User.build(id, "HUATHY");
        return user;
    }

    @PostFilter("filterObject.id%2 == 0")
    @RequestMapping("/lists")
    public List<User> getAllUser() {
        List<User> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            users.add(User.build(i, "嘻嘻" + i));
        }
        return users;
    }

    @PreAuthorize("hasAuthority('READ_INFO')")
    @RequestMapping("/getInfo")
    public Object getInfo() {
        return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }

    /* === 以下是不常用的 只做演示 === */
    // 具备其中一个即可
    @Secured({"ROLE_ADMIN", "ROLE_USER"})
    @RequestMapping("/secured")
    public String secured() {
        return "<h1>HUATHY</h1>";
    }

    @PermitAll
    @RequestMapping("permitAll")
    public String permitAll() {
        return "<h1>permitAll</h1>";
    }

    @DenyAll
    @RequestMapping("DenyAll")
    public String DenyAll() {
        return "<h1>DenyAll</h1>";
    }

    @RolesAllowed({"ROLE_ADMIN", "ROLE_USER"})// 具备其中一个即可
    @RequestMapping("rolesAllowed")
    public String rolesAllowed() {
        return "<h1>rolesAllowed</h1>";
    }
}

四、授权的原理分析

在这里插入图片描述

  • ConfigAttribute在springsecurity中,用户请求一个资源(通常是一个接口或者Java方法)需要的角色会被封装成一个ConfigAttribute对象,在ConfigAttribute中只有一个getAttribute方法,该方法赶回一个String字符串(角色名称)。一般的角色名称都带有一个ROLE_前缀,投票器AccessDecisionVoter所做的事情,其实就是比较用户所具有的角色和请求某个资源所需要的ConfigAttribute之间的关系。
  • AccessDecisionVoterAccessDecisionManager都有众多实现类。在AccessDecisionManager中会挨个遍历AccessDecisionVoter,进而决定是否允许用户方法,因而AccessDecisionVoter和AccessDecisionManager俩者关系类似于AuthenticationProvicder和ProviderManager的关系。

授权实战—权限模型说明1

在前面的案例中,我们配置的URL拦截规则和URL所需要的权限都是通过代码配置的,这样过于死板。如果需要动态的管理权限规则,我们可以将URL拦截规则和访问URL所需的权限都保存到数据库中,这样在不修改代码的情况下只需要吸怪数据库即可对权限进行调整。
用户 < --用户角色表-- > 角色 < --角色菜单表-- > 菜单

库表设计

create table menu
(
    id      int auto_increment
        primary key,
    pattern varchar(100) null
)
    comment '菜单表';

create table menu_role
(
    id  int auto_increment
        primary key,
    rid int not null,
    mid int not null
);

create table role
(
    id      int auto_increment
        primary key,
    name    varchar(255) null,
    name_cn varchar(255) null
);

create table user
(
    id                    int auto_increment
        primary key,
    username              varchar(255) null,
    password              varchar(255) null,
    accountNonExpired     int(1)       null,
    accountNunLocked      int(1)       null,
    credentialsNonExpired int(1)       null,
    enable                int(1)       null
);

create table user_role
(
    id  int auto_increment
        primary key,
    uid int null,
    rid int null
);

数据

insert into role values (101,'superadmin','超级管理员');
insert into role values (102,'admin','管理员');
insert into role values (103,'user','普通用户');

insert into user values (1001,'huathy','{noop}123',0,0,0,0);
insert into user values (1002,'whx','{noop}123',0,0,0,0);
insert into user values (1003,'dy','{noop}123',0,0,0,0);

insert into user_role values (0,1001,101);
insert into user_role values (0,1002,102);
insert into user_role values (0,1003,103);

insert into menu values (1,'/admin/**');
insert into menu values (2,'/user/**');
insert into menu values (3,'/guest/**');

insert into menu_role values (0,101,1);
insert into menu_role values (0,102,2);
insert into menu_role values (0,103,3);

实现

本文只展示了核心代码,详细的参考附录1。

1. MyUserDetailsService

实现自定义UserDetailsService,从数据库获取用户信息。

@Component
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.getUserByUname(username);
        if (ObjectUtils.isEmpty(user)) {
            throw new UsernameNotFoundException("用户名不正确");
        }
        List<Role> roles = userMapper.getRolesByUid(user.getId());
        user.setRoles(roles);
        return user;
    }
}

2. 编写SecurityCfg配置,来自定义URL权限处理。

@EnableWebSecurity
public class SecurityCfg {

    @Autowired
    private CustomerSecurityMetadataSource customerSecurityMetadataSource;
    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        // 1. 获取工厂对象
        ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);
        // 2. 设置自定义URL权限处理
        http.apply(new UrlAuthorizationConfigurer<>(applicationContext)).withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                object.setSecurityMetadataSource(customerSecurityMetadataSource);
                // 是否拒绝公共资源的访问
                object.setRejectPublicInvocations(false);
                return object;
            }
        });
        http.authorizeHttpRequests().anyRequest().authenticated();
        http.formLogin();
        http.csrf().disable();
        return http.build();
    }
}

3. 编写自定义权限元数据CustomerSecurityMetadataSource

需要注意的是此类中的SecurityConfig,是springSecurity官方的SecurityConfig。

@Component
public class CustomerSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    @Autowired
    MenuService menuService;
    // 用来做路径比对
    AntPathMatcher antPathMatcher = new AntPathMatcher();

    /**
     * 自定义动态资源权限元数据信息
     *
     * @param object
     * @return
     * @throws IllegalArgumentException
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 根据当前请求对象获取URI
        String requestURI = ((FilterInvocation) object).getRequest().getRequestURI();
        // 查询菜单对象
        List<Menu> allMenu = menuService.getList(null);
        for (Menu menu : allMenu) {
            if (antPathMatcher.match(menu.getPattern(), requestURI)) {
                String[] roles = new String[menu.getRoles().size()];
                for (int i = 0; i < menu.getRoles().size(); i++) {
                    roles[i] = "ROLE_" + menu.getRoles().get(i).getName();
                }
                return SecurityConfig.createList(roles);
            }
        }
        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

4. MenuService

@Service
public class MenuService {
    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private MenuRoleMapper menuRoleMapper;

    public List<Menu> getList(Object o) {
        List<Menu> menus = menuMapper.selectList(null);
        for (Menu menu : menus) {
            List<Role> roles = menuRoleMapper.getAllMenuRoles(menu.getId());
            if (!CollectionUtils.isEmpty(roles)) {
                menu.setRoles(roles);
            }
        }
        return menus;
    }
}

踩坑

这里有点坑的地方就是SpringSecurity会给角色的权限自动加上ROLE_,即便我加了前缀,他还是会自动加一次。这导致了这里equls的时候匹配失败。所以这里我们取消数据库中的前缀,这样查询出来的用户的角色是不带前缀的(eg:ADMIN)而我们在查询菜单的角色的构建CustomerSecurityMetadataSource元数据的时候给手动加上前缀ROLE_,就像这样子:roles[i] = "ROLE_" + menu.getRoles().get(i).getName();
在这里插入图片描述

附录:

  1. 本文涉及代码部分https://gitee.com/huathy/study-all/tree/master/spring_security_study

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

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

相关文章

ChatGPT-4震撼发布

3月15日消息&#xff0c;美国当地时间周二&#xff0c;人工智能研究公司OpenAI发布了其下一代大型语言模型GPT-4&#xff0c;这是其支持ChatGPT和新必应等应用程序的最新AI大型语言模型。该公司表示&#xff0c;该模型在许多专业测试中的表现超出了“人类水平”。GPT-4, 相较于…

基于Java+Springboot+vue高校资源共享交流平台设计和实现

博主介绍&#xff1a;✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

SpringBoot介绍。

目录 一、SpringBoot简介 1、SpringBoot开发步骤 2、官网构建工程 3、SpringBoot概述 二、配置文件 1、配置文件格式 2、yaml格式 3、yaml配置文件数据读取 三、多环境配置 1、yam文件 2、properties文件 3、命令行启动参数设置 四、SpringBoot整合 1、SpringBo…

界面开发(4)--- PyQt5实现打开图像及视频播放功能

PyQt5创建打开图像及播放视频页面 上篇文章主要介绍了如何实现登录界面的账号密码注册及登录功能&#xff0c;还简单介绍了有关数据库的连接方法。这篇文章我们介绍一下如何在设计的页面中打开本地的图像&#xff0c;以及实现视频播放功能。 实现打开图像功能 为了便于记录实…

OCPC系列 - OCPC介绍扫盲贴来啦

本文对oCPC做个介绍&#xff0c;它是一种智能投放模式&#xff0c;系统通过对广告主转化数据的对接和深度理解&#xff0c;实时预估每一次点击的转化率并基于竞争环境智能出价&#xff0c;通过强化高转化率曝光机会的获取&#xff0c;弱化低转化率曝光机会的展现&#xff0c;以…

力扣-进店却未进行过交易的顾客

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;1581. 进店却未进行过交易的顾客二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行…

文心一言正式对标GPT-4,是青铜还是王者?

昨天&#xff0c;OpenAI正式发布GPT-4模型 号称史上最先进的AI系统 今天&#xff0c;百度文心一言在万众瞩目中闪亮登场 这款产品被视为中国版ChatGPT 在这一个多月内备受关注 文心一言某种程度上具有了对人类意图的理解能力 回答的准确性、逻辑性、流畅性都逐渐接近人类…

Go 微服务开发框架 DMicro 的设计思路

Go 微服务开发框架 DMicro 的设计思路 DMicro 源码地址: Gitee:dmicro: dmicro是一个高效、可扩展且简单易用的微服务框架。包含drpc,dserver等 背景 DMicro 诞生的背景&#xff0c;是因为我写了 10 来年的 PHP&#xff0c;想在公司内部推广 Go, 公司内部的组件及 rpc 协议都…

多模态特征融合:图像、语音、文本如何转为特征向量并进行分类

多模态特征融合前言输入层&#xff0c;数据集转为特征向量图像语音什么是时域信号&#xff0c;什么是频域信号语音信号转换 - 1.傅立叶变换语音信号转换 - 2.梅尔频率倒谱系数文本词袋模型词嵌入模型输出层&#xff0c;多模态模型合并前言 学习多模态的话题可以从深度学习的分…

【YOLOv8/YOLOv7/YOLOv5/YOLOv4/Faster-rcnn系列算法改进NO.57】引入可形变卷积

文章目录前言一、解决问题二、基本原理三、​添加方法四、总结前言 作为当前先进的深度学习目标检测算法YOLOv8&#xff0c;已经集合了大量的trick&#xff0c;但是还是有提高和改进的空间&#xff0c;针对具体应用场景下的检测难点&#xff0c;可以不同的改进方法。此后的系列…

[JS与链表]普通链表

为什么要用链表要储存一系列数据&#xff0c;最常用的数据结构是数组。数组有个缺点就是在中间插入或删除元素需要移动元素&#xff0c;成本很高。什么是链表链表也是有序元素的集合结构。链表中的元素在内存中并不是连续放置的。每个元素都可以理解为一个对象。包含了本身元素…

简单了解JSP

JSP概念与原理概念: Java Server Pages&#xff0c;Java服务端页面一种动态的网页技术&#xff0c;其中既可以定义 HTML、JS、CSS等静态内容&#xff0c;还可以定义Java代码的动态内容JSP HTML Java, 用于简化开发JSP的本质上就是一个ServletJSP 在被访问时&#xff0c;由JSP容…

博途PLC开放式以太网通信TRCV_C指令应用编程(运动传感器UDP通信)

博途PLC开放式以太网通信TSENG_C指令应用,请参看下面的文章链接: 博途PLC 1200/1500PLC开放式以太网通信TSEND_C通信(UDP)_plc的udp通信_RXXW_Dor的博客-CSDN博客开放式TSEND_C通信支持TCP 、UDP等,关于TSEND_C的TCP通信可以参看下面这篇文章:博途PLC 1200/1500PLC开放式…

opencv识别车道线(霍夫线变换)

目录1、前言2、霍夫线变换2.1、霍夫线变换是什么&#xff1f;2.2、在opencv中的基本用法2.2.1、HoughLinesP函数定义2.2.2、用法3、识别车道3.1、优化3.1.1、降噪3.1.2、过滤方向3.1.3、截选区域3.2、测试其它图片3.2.1、代码3.2.2、图片13.2.3、图片23.2.4、图片31、前言 最近…

C++模拟实现红黑树

目录 介绍----什么是红黑树 甲鱼的臀部----规定 分析思考 绘图解析代码实现 节点部分 插入部分分步解析 ●父亲在祖父的左&#xff0c;叔叔在祖父的右&#xff1a; ●父亲在祖父的右&#xff0c;叔叔在祖父的左&#xff1a; 测试部分 整体代码 介绍----什么是红黑树 红…

2023年江苏省职业院校技能大赛中职网络安全赛项试卷-教师组任务书

2023年江苏省职业院校技能大赛中职网络安全赛项试卷-教师组任务书 一、竞赛时间 9:00-12:00&#xff0c;12:00-15:00&#xff0c;15:00-17:00共计8小时。 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一阶段 基础设施设置与安全加固、网络安全事件响应、数…

链表相关oj题

1.Leetcode203 移除链表元素 解题思路&#xff1a;从头节点开始进行元素删除&#xff0c;每删除一个元素&#xff0c;需要重新链接节点 struct ListNode* removeElements(struct ListNode* head, int val){struct ListNode*dummyheadmalloc(sizeof(struct ListNode));dummyhea…

spring5(四):IOC 操作 Bean 管理(基于注解方式)

IOC操作Bean管理&#xff08;基于xml方式&#xff09;前言一、注解1、概述二、入门案例1、Bean 的创建2、Bean的自动装配2.1 Autowired2、Qualifie3、Resource4、Value3、扫描组件3.1 配置文件版3.2 注解版4、测试前言 本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心…

Mysql常用命令

mysql连接&#xff1a; [roothost]# mysql -u root -p Enter password:******创建数据库&#xff1a; CREATE DATABASE 数据库名&#xff1b; 删除数据库&#xff1a; drop database 数据库名; 使用mysqladmin删除数据库&#xff1a; [roothost]# mysqladmin -u root -p dr…

【数据结构】链表OJ(二)

Yan-英杰的博客 悟已往之不谏 知来者之可追 目录 一、反转链表 二、合并两个有序链表 三、链表分割 四、链表的回文结构 一、反转链表 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1] 示例 3&#xf…
最新文章