springsecurity的学习(四):实现授权

简介

springsecurity的授权,自定义授权失败的处理,跨域的处理和自定义权限校验方法的介绍

授权

权限系统作用

在后台进行用户权限的判断,判断当前用户是否有相应的权限,必须具有所需的权限才能进行相应的操作,以此达到不同的用户可以使用不同的功能。

流程

会使用springsecurity默认的FilterSecurityInterceptor来进行权限校验,会从SecurityContextHolder获取其中的Authentication,从Authentication中获取权限的信息,判断当前用户是否拥有当前资源所需的权限。

实现方式

springsecurity提供了基于注解的权限控制方案,使用注解去指定访问对应的资源所需的权限。
需要在配置类中添加注解@EnableGlobalMethodSecurity 注解开启相关的配置
开启后,即可在controller的接口上添加使用springsecurity的权限相关的注解。如
@PreAuthorize("hasAuthority('权限字符串')"):可以判断当前访问接口的用户是否有这个权限

数据库查询权限

rabc权限模型

基于角色的权限控制。

创建表

需要5张表
在这里插入图片描述
创建语句:

CREATE TABLE `sys_menu` (`id` bigint NOT NULL AUTO_INCREMENT,`menu_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '菜单名',`path` varchar(200) DEFAULT NULL COMMENT '路由地址',`component` varchar(255) DEFAULT NULL COMMENT '组件路径',`visible` char(1) DEFAULT '0' COMMENT '菜单状态(0显示 1隐藏)',`status` char(1) DEFAULT '0' COMMENT '菜单状态(0正常 1停用)',`perms` varchar(100) DEFAULT NULL COMMENT '权限标识',`icon` varchar(100) DEFAULT '#' COMMENT '菜单图标',`create_by` bigint DEFAULT NULL,`create_time` datetime DEFAULT NULL,`update_by` bigint DEFAULT NULL,`update_time` datetime DEFAULT NULL,`del_flag` int DEFAULT '0' COMMENT '是否删除(0未删除 1已删除)',`remark` varchar(500) DEFAULT NULL COMMENT '备注',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='权限表';#################################
CREATE TABLE `sys_role` (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(128) DEFAULT NULL,`role_key` varchar(100) DEFAULT NULL COMMENT '角色权限字符串',`status` char(1) DEFAULT '0' COMMENT '角色状态(0正常 1停用)',`del_flag` int DEFAULT '0' COMMENT 'del_flag',`create_by` bigint DEFAULT NULL,`create_time` datetime DEFAULT NULL,`update_by` bigint DEFAULT NULL,`update_time` datetime DEFAULT NULL,`remark` varchar(500) DEFAULT NULL COMMENT '备注',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='角色表';
#################################
CREATE TABLE `sys_role_menu` (`role_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID',`menu_id` bigint NOT NULL DEFAULT '0' COMMENT '菜单id',PRIMARY KEY (`role_id`,`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
#################################
CREATE TABLE `sys_user` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',`user_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '用户名',`nick_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '昵称',`password` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '密码',`status` char(1) DEFAULT '0' COMMENT '账号状态(0正常 1停用)',`email` varchar(64) DEFAULT NULL COMMENT '邮箱',`phonenumber` varchar(32) DEFAULT NULL COMMENT '手机号',`sex` char(1) DEFAULT NULL COMMENT '用户性别(0男,1女,2未知)',`avatar` varchar(128) DEFAULT NULL COMMENT '头像',`user_type` char(1) NOT NULL DEFAULT '1' COMMENT '用户类型(0管理员,1普通用户)',`create_by` bigint DEFAULT NULL COMMENT '创建人的用户id',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`update_by` bigint DEFAULT NULL COMMENT '更新人',`update_time` datetime DEFAULT NULL COMMENT '更新时间',`del_flag` int DEFAULT '0' COMMENT '删除标志(0代表未删除,1代表已删除)',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';
#################################
CREATE TABLE `sys_user_role` (`user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户id',`role_id` bigint NOT NULL DEFAULT '0' COMMENT '角色id',PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
根据userid查询权限
select distinct m.`perms`
fromsys_user_role urleft join `sys_role` r on ur.`role_id` = r.`id`left join `sys_role_menu` rm on ur.`role_id` = rm.`role_id`left join `sys_menu` m on m.`id` = rm.`menu_id`
where user_id= 用户idand r.`status` = 0and m.`status` = 0

在这里插入图片描述

Menu 类
package com.springSecurityTest.common;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.Date;@TableName(value="sys_menu") //指定表名,避免等下mybatisplus的影响
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
//Serializable是官方提供的,作用是将对象转化为字节序列
public class Menu implements Serializable {private static final long serialVersionUID = -54979041104113736L;@TableIdprivate Long id;/*** 菜单名*/private String menuName;/*** 路由地址*/private String path;/*** 组件路径*/private String component;/*** 菜单状态(0显示 1隐藏)*/private String visible;/*** 菜单状态(0正常 1停用)*/private String status;/*** 权限标识*/private String perms;/*** 菜单图标*/private String icon;private Long createBy;private Date createTime;private Long updateBy;private Date updateTime;/*** 是否删除(0未删除 1已删除)*/private Integer delFlag;/*** 备注*/private String remark;
}
MenuMapper类
package com.springSecurityTest.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.springSecurityTest.common.Menu;
import org.apache.ibatis.annotations.Mapper;import java.util.List;
@Mapper
public interface MenuMapper extends BaseMapper<Menu> {List<String> selectMemusByUserId(Long userId);
}
menu.xml
<?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.springSecurityTest.mapper.MenuMapper"><select id="selectMemusByUserId" resultType="java.lang.String">select distinct sys_menu.perms from sys_user_role
left join sys_role
on sys_user_role.role_id = sys_role.id
left join sys_role_menu
on sys_user_role.role_id = sys_role_menu.role_id
left join sys_menu
on sys_menu.id = sys_role_menu.menu_id
where user_id = #{userid}
and sys_role.`status` = 0</select>
</mapper>

springsecurity授权

UserDetailsServiceImpl类

把权限信息放入到loginuser中

package com.springSecurityTest.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.springSecurityTest.mapper.MenuMapper;
import com.springSecurityTest.mapper.UserMapper;
import com.springSecurityTest.common.LoginUser;
import com.springSecurityTest.common.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;@Service
public class UserDetailsServiceImpl implements UserDetailsService {@ResourceUserMapper userMapper;@Resourceprivate MenuMapper menuMapper;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(User::getUserName,s);User user = userMapper.selectOne(lambdaQueryWrapper);if (Objects.isNull(user)){throw new RuntimeException("用户名或密码错误");}//查询权限List<String> list = menuMapper.selectMemusByUserId(user.getId());return new LoginUser(user,list);}
}
LoginUser 类

添加权限的属性,重写getAuthorities方法。把permissions中的权限信息封装成simpleGrantauthority对象

package com.springSecurityTest.common;import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails {private User user;private List<String> permissions;public LoginUser(User user,List<String> permissions){this.user = user;this.permissions = permissions;}@JSONField(serialize = false)private List<GrantedAuthority> authorities;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if(authorities!= null){return authorities;}authorities = new ArrayList<>();for(String permission:permissions){SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permission);authorities.add(simpleGrantedAuthority);}return authorities;}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getUserName();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
JwtAuthenticationTokenFilter 类

给usernamePasswordAuthenticationToken 对象添加权限

package com.springSecurityTest.filter;import com.springSecurityTest.common.LoginUser;
import com.springSecurityTest.utils.JwtUtil;
import com.springSecurityTest.utils.RedisCache;
import io.jsonwebtoken.Claims;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import sun.plugin.liveconnect.SecurityContextHelper;import javax.annotation.Resource;
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.Objects;@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Resourceprivate RedisCache redisCache;@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {String token = httpServletRequest.getHeader("token");if (!StringUtils.hasText(token)) {filterChain.doFilter(httpServletRequest, httpServletResponse);return;}String userId;try {System.out.println(JwtUtil.parseJWT(token));Claims claims = JwtUtil.parseJWT(token);userId = claims.getSubject();} catch (Exception e) {e.printStackTrace();throw new RuntimeException("token非法");}String redisKey = "token:" + userId;LoginUser loginUser = redisCache.getCacheObject(redisKey);if(Objects.isNull(loginUser)){throw new RuntimeException("用户未登录");}UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null, loginUser.getAuthorities());SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);filterChain.doFilter(httpServletRequest,httpServletResponse);}}
controller类
package com.springSecurityTest.controller;import com.springSecurityTest.common.User;
import com.springSecurityTest.mapper.UserMapper;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.List;@RestController
public class dark {
@RequestMapping("/dark")
@PreAuthorize("@hasAuthority('system:dept:list')")public String dark(){return "it's too dark!";
}}

自定义失败

在springsecurity中,如果在认证或者授权的过程中出现了异常,会被ExceptionTranslationFilter捕获,然后调用如下对象的方法处理异常:

  • AuthenticationEntryPoint对象的方法会对认证过程中出现的异常进行处理
  • AccessDeniedHandler对象的方法会对授权过程中出现的异常进行处理。

要自定义异常处理,只需要自定义AuthenticationEntryPoint和AccessDeniedHandler,然后配置给springsecurity。

AccessDeniedHandlerImpl类
package com.springSecurityTest.handler;import com.alibaba.fastjson.JSON;
import com.springSecurityTest.common.ResponseResult;
import com.springSecurityTest.utils.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Service;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Service
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {ResponseResult result = new ResponseResult(HttpStatus.FORBIDDEN.value(), "权限不足");String json = JSON.toJSONString(result);WebUtils.renderString(httpServletResponse,json);}
}
AuthenticationEntryPointImpl类
package com.springSecurityTest.handler;import com.alibaba.fastjson.JSON;
import com.springSecurityTest.common.ResponseResult;
import com.springSecurityTest.utils.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Service;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Service
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {ResponseResult result = new ResponseResult(HttpStatus.UNAUTHORIZED.value(), "用户认证失败请重新登录");String json = JSON.toJSONString(result);WebUtils.renderString(httpServletResponse,json);}
}
WebUtils

上面两个类中用到的工具类

package com.springSecurityTest.utils;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class WebUtils {/*** 将字符串渲染到客户端** @param response 渲染对象* @param string 待渲染的字符串* @return null*/public static String renderString(HttpServletResponse response, String string) {try{response.setStatus(200);response.setContentType("application/json");response.setCharacterEncoding("utf-8");response.getWriter().print(string);}catch (IOException e){e.printStackTrace();}return null;}
}
SecurityConfig

添加两个异常处理器

package com.springSecurityTest.config;import com.springSecurityTest.filter.JwtAuthenticationTokenFilter;
import com.springSecurityTest.handler.AuthenticationEntryPointImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import javax.annotation.Resource;
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Resourceprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;@Resourceprivate AuthenticationEntryPointImpl authenticationEntryPoint;@Resourceprivate AccessDeniedHandler accessDeniedHandler;@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
//testgit@Overrideprotected void configure(HttpSecurity http) throws Exception {http//由于是前后端分离项目,所以要关闭csrf.csrf().disable()//由于是前后端分离项目,所以session是失效的,我们就不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()//指定让spring security放行登录接口的规则.authorizeRequests()// 对于登录接口 anonymous表示允许匿名访问.antMatchers("/user/login").anonymous().antMatchers("/dark").hasAuthority("system:test:list")// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();http.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class);//配置异常处理器http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler);
http.cors();}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}

跨域问题

同源策略:协议,域名,端口号要一致
在应用中启用了Spring Security,它默认会对所有的请求进行拦截和验证。这意味着,即使Spring Boot配置允许了CORS,Spring Security的默认配置也可能阻止跨域请求,因为它会检查每一个请求是否带有有效的认证信息。
为了使Spring Security与CORS协同工作,通常需要在Spring Security的配置中显式地允许CORS请求。

springboot配置
package com.springSecurityTest.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {@Override//重写spring提供的WebMvcConfigurer接口的addCorsMappings方法public void addCorsMappings(CorsRegistry registry) {// 设置允许跨域的路径registry.addMapping("/**")// 设置允许跨域请求的域名.allowedOriginPatterns("*")// 是否允许cookie.allowCredentials(true)// 设置允许的请求方式.allowedMethods("GET", "POST", "DELETE", "PUT")// 设置允许的header属性.allowedHeaders("*")// 跨域允许时间.maxAge(3600);}
}
springsecurity配置

在springsecurity的配置类SecurityConfig中,重写configure方法,加上http.cors();

自定义权限校验方法

package com.springSecurityTest.expression;import com.springSecurityTest.common.LoginUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;import java.util.List;@Component("MyExpressionRoot ")
public class MyExpressionRoot {public boolean hasAuthority(String authority){Authentication authentication = SecurityContextHolder.getContext().getAuthentication();LoginUser loginUser  = (LoginUser) authentication.getPrincipal();List<String> permissions = loginUser.getPermissions();return permissions.contains(authority);}
}
使用自定义权限校验方法
package com.springSecurityTest.controller;import com.springSecurityTest.common.User;
import com.springSecurityTest.mapper.UserMapper;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.List;@RestController
public class dark {@ResourceUserMapper userMapper;
@RequestMapping("/dark")
@PreAuthorize("@MyExpressionRoot .hasAuthority('system:dept:list')")public String dark(){return "it's too dark!";
}
@GetMapping("/getUser")public List<User> usertest(){List<User> users = userMapper.selectList(null);System.out.println(users);return users;
}
}

CSRF

跨站请求伪造,是web常见攻击之一,依靠的是cookie中携带的认证信息,使用token可以不用担心csrf攻击,因为token不存储在cookie中,而且前端把token设置到请求头中访问网站资源。

扩展

如果登录页面还有验证码,那还可以在UsernamePasswordAuthenticationFilter之前再写一个验证码的过滤器,组成过滤器链。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【运维】JetBrains Gateway (Pycharm) SSH免密连接,改为免密连接

一直要求输入密码&#xff0c;很烦人&#xff1a; 如何免密连接&#xff1f; 1 重新打开gateway&#xff0c;来到这个界面点新建连接&#xff1a; 2 点这里设置&#xff1a; 3 在这一页&#xff0c;你可以改你的所有配置&#xff0c;只要设置为password并且保存密码&…

MySQL第6讲--DQL(数据查询语言)的基本操作之基本和条件查询

文章目录 前言DQL(数据查询语言)基本操作查询操作基本查询示例1&#xff1a;查询表格的name&#xff0c;age&#xff0c;并返回&#xff1b;示例2&#xff1a;查询表格中的所有字段&#xff1b;示例3&#xff1a;查询所有员工的工号并返回&#xff0c;起别名&#xff1b;示例4&…

基于Python的去哪儿网数据采集与分析可视化大屏设计与实现

摘要 本文旨在介绍如何利用Python进行去哪儿网景点数据的采集与分析。通过采集去哪儿网上的景点数据&#xff0c;我们可以获取大量的旅游相关信息&#xff0c;并基于这些数据进行深入分析和洞察&#xff0c;为旅游行业、市场营销策略以及用户个性化推荐等提供支持。 本文将使用…

实现挂机会议

png py文件 import os import pyautogui import time from typing import Callable, Tuple from datetime import datetime import cv2 import schedule#通过图像模板匹配在屏幕上找到指定区域并操作 def imgAutoClick(tempFile: str, whatDo: Callable[[Tuple[int, int, int…

【机器学习】混淆矩阵(Confusion Matrix)

一、混淆矩阵 True Negative (TN)&#xff1a; 真负类&#xff0c;样本的真实类别是负类&#xff0c;并且模型将其识别为负类&#xff0c;cm[0][0]。False Positive (FP)&#xff1a; 假正类&#xff0c;样本的真实类别是负类&#xff0c;但是模型将其识别为正类&#xff0c;cm…

【C语言】常用函数汇总表

目录 1. C语言常用函数汇总表&#xff08;概念功能&#xff09;1.1 输入/输出函数&#xff08;<stdio.h>&#xff09;1.2 字符串操作函数&#xff08;<string.h>&#xff09;1.3 内存管理函数&#xff08;<stdlib.h>&#xff09;1.4 数学函数&#xff08;<…

【原创】java+springboot+mysql学业跟踪指导管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

Unity物理模块 之 ​2D刚体

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正​ 1.刚体是什么 在 Unity 中&#xff0c;刚体&#xff08;Rigidbody&#xff09; 是物理引擎中最基本的组件之一&#x…

根据字典值回显,有颜色的

背景 本项目以若依前端vue2版本为例&#xff0c;项目中有根据字典值回显文本的函数selectDictLabel&#xff0c;但是有时候我们需要带颜色的回显&#xff0c;大概这样的 用法 <template v-slotscope><dict-label :options"dangerLevelOptions" :value&qu…

XXL-JOB分布式定时任务框架快速入门

文章目录 前言定时任务分布式任务调度 1、XXL-JOB介绍1.1 XXL-JOB概述1.2 XXL-JOB特性1.3 整体架构 2、XXL-JOB任务中心环境搭建2.1 XXL-JOB源码下载2.2 IDEA导入xxljob工程2.3 初始化数据库2.4 Docker安装任务管理中心 3、XXL-JOB任务注册测试3.1 引入xxl-job核心依赖3.2 配置…

deepin V23前瞻丨deepin V23与飞腾腾锐D3000完成适配,持续丰富deepin操作系统硬件生态

查看原文 近日&#xff0c;自主核心芯片研发飞腾公司宣布与deepin V23完成适配&#xff0c;包含飞腾新一代桌面CPU飞腾腾锐D3000、FT-2000/64、桌面芯片飞腾腾锐D2000等多款产品&#xff0c;为用户带来更智能、安全、稳定的使用体验。 飞腾腾锐D3000集成8个飞腾自主研发的新一…

Golang多版本环境安装并存

1. 准备 请先安装最新版本的Go&#xff0c;详见 https://go.dev/doc/install go version2. 配置镜像加速 go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct3. 安装Go指定版本 Golang官方提供dl工具来实现多版本的Go环境管理&#xff0c;详见项目&#…

小白零基础学数学建模应用系列(二):基于Python的共享单车系统建模与仿真分析

共享单车系统作为一种绿色环保的出行方式&#xff0c;已成为现代城市公共交通的重要组成部分。本文将使用Python编程语言&#xff0c;并结合modsim库&#xff0c;构建并仿真一个共享单车系统的模型&#xff0c;通过这一过程来展示如何进行系统的建模与分析。 文章目录 一、背景…

虚拟化—XenServer安装教程详细(附客户端连接)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…

Unity 编写自己的aar库,接收Android广播(broadcastReceiver)并传递到Unity

编写本文是因为找了很多文章&#xff0c;都比较片段&#xff0c;不容易理解&#xff0c;对于Android新手来说理解起来不友好。我这里写了一个针对比较小白的文章&#xff0c;希望有所帮助。 Android端 首先还是先来写Android端&#xff0c;我们新建一个Android空项目&#xf…

重新定义AI绘画体验,Midjourney全面进阶指令指南

前言 在人工智能绘画的世界里,Midjourney无疑是一颗璀璨的明珠。今天,就让我们一同探索这个神奇工具的奥秘,开启一段全新的AI绘画之旅。 一、奇妙之门:Midjourney的本质 与众不同的是,Midjourney并非一个单纯的软件,而是栖身于Discord平台的一位"云端画师"。有别于本…

【微信小程序】网络数据请求

1. 小程序中网络数据请求的限制 2. 配置 request 合法域名 3. 发起 GET 请求 调用微信小程序提供的 wx.request() 方法&#xff0c;可以发起 GET 数据请求&#xff0c;示例代码如下&#xff1a; 4. 发起 POST 请求 调用微信小程序提供的 wx.request() 方法&#xff0c;可以…

(计算机网络)网络层

目录 一.网络层提供哪种服务 二.两种服务的比较 三.ip协议 四.ip地址 五.ip地址的分类 六.子网掩码 七.路由器介绍 一.网络层提供哪种服务 1.ip地址--唯一的标识互联网上的某一台主机 2. 虚电路&#xff1a;虚拟的电路 二.两种服务的比较 ip数据报&#xff0c;不需要建…

Visual Studio Code 安装与 C/C++ 语言运行总结

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; Visual Studio Code&#xff08;简称 VS Code&#xff09;是由微软开发的一款轻量级、强大的代码编辑器&#xff0c;支持多种编程语言和开发框架。由于其丰富的插件生态系统和灵活的配置选项&#xff0c;VS…

Layui——隐藏表单项后不再进行验证

目录 修改后的部分代码 修改后的完整代码 我编辑用户信息和添加新用户用的是同一个表单&#xff0c;不同的是编辑用户信息里没有密码项和确认密码项&#xff0c;但是把它们隐藏后仍然要进行验证&#xff0c;也就是说它们俩的验证并没有随着表单项的隐藏而关闭。原因&#xf…