SpringSecurity 入门

一步一步来。

集各家之所长,师从 尚硅谷、狂神、三更草堂;如果文中发现跟他们有类似的,不用想,就是他们那里拿来的

事先声明,本人很菜,其中有说的误人子弟的,请大家指出来。

环境:

idea2020.1 SpringBoot2.6.3 MyBatisPlus Maven3.6.3 SpringSecurity Lombok MySQL8.0.23…

总述:

先根据三家总的说一下,大致可以划分为两类: **1.**前后端不分离 **2.**前后端分离

SpringSecurity概述

这一部分就是简述它的。

它是一款重量级安全框架,说起安全框架就立马想起两个关键词“认证”和“授权”。

一般来说,Web 应用的安全性包括**用户认证(Authentication)和用户授权(Authorization)**两个部分,这两点也是 Spring Security 重要核心功能。 英语单词务必给我记住

(1)用户认证指的是:就是系统认为用户是否能登录,账号密码匹配的问题

(2)用户授权指的是:就是系统判断用户是否有权限去做某些事情,比如vip权限可以干啥,普通用户却不行

例子:老婆与卧室

老婆在卧室里面睡觉,

情况一:这时响起了敲门声,通过猫眼看是你(老公),认证通过,授权老公身份,允许进入卧室,嘿嘿嘿;

情况二:通过猫眼看是隔壁老王(熟人),认证通过,没有权限跟老婆一起进卧室,老王黯然神伤;

情况三:陌生人,客厅门都进不来

同台竞争对手:Shiro(大致学过,估计忘记了,轻量级)

技术选型推荐:SSM + Shiro SpringBoot + SpringSecurity

-前后端不分离

下面代码块中标记有项目代码的,就可作为练手的,即写进项目自行测试的(记号为 @@txf)

–入门案例

新建一个SpringBoot项目

目录结构如图:

需要引入的依赖:@@txf

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--先把这个注释掉-->
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-security</artifactId>-->
<!--        </dependency>--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency></dependencies>

HelloController.java @@txf

@Controller
public class HelloController {@RequestMapping("/hello")public String HelloPage() {return "hello";}
}

hello.html @@txf(templates下面)

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>hello</title>
</head>
<body>这是hello界面
</body>
</html>

启动项目,浏览器运行 localhost:8080/hello

会直接跳转到templates下面的hello.html界面

然后将pom.xml里面的security依赖加上,刷新maven,重新运行项目,浏览器输入 localhost:8080/hello

出现如下界面:

我们发现地址栏自动发生了变化,界面也不是hello了,在控制台我们甚至可以看到这么一出

这就说明,SpringSecurity拦截到了我们的/hello请求,发现没有进行认证(登录),就给我们默认处理,跳转到了这个界面(默认的)。

此时,username输入user, password复制控制到输出的密码,就可以进行登录了。以上是Security默认生成了密码,为第一种方式;第二种方式:在配置文件中自己配置username和password。如下

application.properties或者application.yml @@txf

server.port=8080 #这是我自己配置的端口号,爱写不写,还是养成写的好习惯#在配置文件中配置账号密码
spring.security.user.name=txf
spring.security.user.password=123456

配置完重新启动项目,发现控制台不会自动生成密码了,此时localhost:8080/hello跳转去/login时用我们在配置文件中的账号密码登录即可。登录完成就可以看到hello了。

你如果账号密码输入的不正确,肯定是通过不了的,自己动手实践去。

如果这样写账号密码的话,是固定的,实际场景肯定是从数据库中查到的!!!!!!!!

–加入数据库

引入依赖 @@txf

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.23</version>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version>
</dependency>

创建数据库test,创建表customer: @@txf(数据库建表)

DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer`  (`id` int NOT NULL AUTO_INCREMENT,`realName` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '真实姓名',`username` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '账号',`password` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',`age` int NULL DEFAULT 20 COMMENT '年龄',`adress` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '家庭住址',`gender` int NULL DEFAULT 1 COMMENT '性别',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of customer
-- ----------------------------
INSERT INTO `customer` VALUES (1, '田小锋', 'txfnbnbnb', '123456', 20, '湖北省荆门市京山市', 1);

里面加入了一条数据,那就是我了。。。

配置文件中肯定是要修改的,加入下面这些 @@txf

#数据库
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=GMT%2B8&useUnicode=true&allowMultiQueries=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Drivermybatis-plus.type-aliases-package=com.feng.securitydemo01.pojo
mybatis-plus.mapper-locations=classpath:/mapper/*.xml

实体类pojo包 @@txf

package com.feng.securitydemo01.pojo;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("customer")
public class Customer {@TableIdprivate Integer id;private String realname;private String username;private String password;private Integer age;private String adress;private Integer gender;
}

Mapper接口 @@txf

package com.feng.securitydemo01.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.feng.securitydemo01.pojo.Customer;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;@Mapper
@Repository
public interface CustomerMapper extends BaseMapper<Customer> {}

–原理初探

页面提交方式必须为 post 请求

先放一张别人的图,SpringSecurity本质是一个过滤器链。

大致流程:

各个过滤器及其顺序:

上图只需要明白流程,不要记!大致是从顶部先一步一步调用走到底部,然后从底部返回值一步一步回到顶部

我们在它默认的登录页面输入的username和password,经过上图一系列过程,最终到UserDetailsService中。这个UserDetailsService是一个接口。

package com.feng.securitydemo01.service.Impl;@Service //这个就不用多说了吧,交给Spring容器来管理,替换掉默认的
public class MyUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//在这个方法里面,根据前端页面传过来的username,我们去数据库做一系列查询操作,//返回值是一个UserDetails,}
}//==================下面是具体实现=====================       @@txf
package com.feng.securitydemo01.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.feng.securitydemo01.mapper.CustomerMapper;
import com.feng.securitydemo01.pojo.Customer;
import com.feng.securitydemo01.pojo.LoginCustomer;
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.Objects;@Service
public class MyUserDetailsService implements UserDetailsService {@Autowiredprivate CustomerMapper customerMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {QueryWrapper<Customer> wrapper = new QueryWrapper<>();wrapper.eq("username",username);Customer customer = customerMapper.selectOne(wrapper);if (Objects.isNull(customer) ) { //空的,说明查不到throw new UsernameNotFoundException("用户名不存在!");}//查到了,注意,这里现在我们不需要密码校验,直接封装为UserDetails返回就行了//UserDetail我们自己实现return new LoginCustomer(customer);}
}

UserDetails

//点进源码发现这也是一个接口,是接口我们就可以去实现它,来完成自定义(虽然它已经有实现类了)
public interface UserDetails extends Serializable {Collection<? extends GrantedAuthority> getAuthorities();String getPassword();String getUsername();boolean isAccountNonExpired();boolean isAccountNonLocked();boolean isCredentialsNonExpired();boolean isEnabled();
}
//===============================源码===========================================
//LoginCustomer.java   自定义实现UserDetails接口
//=====================具体实现===========       @@txf
package com.feng.securitydemo01.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;@Data
@AllArgsConstructor
public class LoginCustomer implements UserDetails {private Customer customer; //存入我们根据username查到的customer@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() { //获取权限,先不看return null;}@Overridepublic String getPassword() {return customer.getPassword();}@Overridepublic String getUsername() {return customer.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {  //能使用return true;}
}

–流程分析

根据上面的流程图文字解析:

  1. 根据传过来的username和password构造UsernamePasswordAuthenticationToken;(UsernamePasswordAuthenticationToken继承自AbstractAuthenticationToken,抽象类AbstractAuthenticationToken实现Authentication接口。)

  2. 调用方法ProviderManager.authenticate(参数类型是Authentication) ,所以可以传进去上面构造的User…Token。

 public Authentication authenticate(Authentication authentication) throws AuthenticationException {..........................Iterator var9 = this.getProviders().iterator();while(var9.hasNext()) {.......//这里就是AbstractUserDetailsAuthenticationProvider的authenticate,result = provider.authenticate(authentication);.......}抽象类AbstractUserDetailsAuthenticationProvider的authenticate,
user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
  1. AbstractUserDetailsAuthenticationProvider的子类DaoAuthenticationProvider.retrieveUser()
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {.................//这里用上了我们的UserDetailsService!!!!!!!!!!!//是不是就返回了UserDetails!!!UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);...................}//看看这个方法是干什么的???????????
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {if (authentication.getCredentials() == null) {this.logger.debug("Failed to authenticate since no credentials provided");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));} else {String presentedPassword = authentication.getCredentials().toString();//=========================// this.passwordEncoder 密码校验,知道为什么不用你自己写了吧。自定义也行//============================if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {this.logger.debug("Failed to authenticate since password does not match stored value");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}}}

–加密

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id “null”

还有一种是什么not like 什么什么的,

//密码加密
//假如数据库泄露了,密码如果是明文存储,那还搞个鬼子!!!!!!!!!所以,安全框架嘛,必然考虑得如此周到
/*
我们存进数据库里面的密码,那就肯定是要加密的了,即存进去的是密文                   @@txf
*/
package com.feng.securitydemo01.config;        import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}
}

//加密测试
@Testpublic void test() {String s = "123456";String s1 = encoder.encode(s);String s2 = encoder.encode(s);System.out.println(s1 +" " + s2);}
//输出结果,居然不一样!!!!这我们就不深究了。。。。反正都是123456
$2a$10$ipQhvDpdCZrpAc.JR7og/.l.3EpPovD2NlMcpUnYH4xv8inMemhI6 $2a$10$LDPVG9vvJvjBHsVfgLv0RO.2sq5LIscT01WBaybTf7bpQ.XHFOASa//密码匹配测试@Testpublic void test() {String s = "123456";boolean matches = encoder.matches(s,"$2a$10$ipQhvDpdCZrpAc.JR7og/.l.3EpPovD2NlMcpUnYH4xv8inMemhI6");System.out.println(matches);  //true}

所以数据库中的123456要改咯,改成密文。将配置文件中自定义的username和password注掉,再来启动项目测试。这时候输入的就是数据库中的了。

数据中:

1    田小锋 txf $2a$10$ipQhvDpdCZrpAc.JR7og/.l.3EpPovD2NlMcpUnYH4xv8inMemhI6    20  湖北省荆门市京山市   1

启动项目:localhost:8080/hello 这时候输入自己的账号密码即可。

仔细看上面两个流程图,流程一定得明白,源码才勉强看得懂

–小项目

在上面的基础之上,改造页面,权限访问那些

授权基本流程

​ 在SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验。在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后获取其中的权限信息。当前用户是否拥有访问当前资源所需的权限。

​ 所以我们在项目中只需要把当前登录用户的权限信息也存入Authentication。

​ 然后设置我们的资源所需要的权限即可

数据添加:(后面两个的密码是txf123)

用户拥有某一种角色,角色对应有哪些权限。上面这些表的意思。(走后门、查找、浏览)三种权限,admin所有权限都有,vip只有两种,user只有一种。四张表的名字分别是customer,cusrole,cusauth,cusroleauth。

有了权限,我们需要从数据库里面根据customer的id查询该用户所拥有的权限名称。

SELECT authname
FROM cusauth
WHERE authid in (SELECT authid FROM cusroleauthWHERE roleid = (SELECT roleid FROM customerWHERE id = 1)
)#这里可不可以请大佬帮我换一种方式查出来。。。本人着实有点愚钝

Mapper接口里面 @@txf

@Mapper
@Repository
public interface CustomerMapper extends BaseMapper<Customer> {List<String> getAuthorizaById(Integer id);
}

Mapper.xml ( resourses下的mapper目录里面CustomerMapper.xml) @@txf

<?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.feng.securitydemo01.mapper.CustomerMapper"><select id="getAuthorizaById" resultType="string" parameterType="int">SELECT authnameFROM cusauthWHERE authid in (SELECT authidFROM cusroleauthWHERE roleid = (SELECT roleidFROM customerWHERE id = #{id}))</select></mapper>

修改

MyUserDetailsService和loginCustomer @@txf

//DetailsService
//查询权限封装进去
List<String> authoriza = customerMapper.getAuthorizaById(customer.getId());
//查到了,注意,这里现在我们不需要密码校验,直接封装为UserDetails返回就行了
//UserDetail我们自己实现
return new LoginCustomer(customer,authoriza);//LoginUserDetailsService.java
private Customer customer;
private List<String> myAuthorities;
//存储SpringSecurity所需要的权限信息的集合
private List<GrantedAuthority> authorities;public LoginCustomer(Customer customer, List<String> myAuthorities) {this.customer = customer;this.myAuthorities = myAuthorities;}@Override
public Collection<? extends GrantedAuthority> getAuthorities() {if(authorities!=null){return authorities;}//myAuthoritiesauthorities = myAuthorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());return authorities;
}
@EnableGlobalMethodSecurity(prePostEnabled = true)
//主启动类上添加此注解,先开启相关配置。

HelloController @@txf

package com.feng.securitydemo01.controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class HelloController {@RequestMapping({"/","/index"})public String index() {return "index";}@RequestMapping("/toLogin")public String toLogin() {return "login";}@GetMapping("/noauth")public String accessDenyPage(){return "noauth"; }@RequestMapping("/houmen")@PreAuthorize("hasAuthority('zouhoumen')") //看登录的用户是否有此权限。public String houmen() {return "admin/houmen";}@RequestMapping("/find")@PreAuthorize("hasAuthority('find')")public String find() {return "vip/find";}@RequestMapping("/hello")@PreAuthorize("hasAuthority('look')")public String HelloPage() {return "hello";}
}

此外,我们希望对首页,不需要登录就能访问怎么做呢? @@txf

@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}//================这中间配置即可@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable()  //关闭csrf.formLogin()
//                .usernameParameter("自己根据情况写") 自定义表单的username,password
//                .passwordParameter("自己情况写")//还有rememberme、logout功能,极其简单,这里就不说了.loginProcessingUrl("/login")  //登录的url.successForwardUrl("/index")   //登录成功后走/index.and().authorizeRequests().antMatchers("/","/index","/toLogin").permitAll()  //"/","/index"放行.anyRequest().authenticated(); //所有请求都需要认证//配置403处理器,修改response中的内容,//http.exceptionHandling().accessDeniedHandler(myAccessFail);//自定义403页面,前后端不分离http.exceptionHandling().accessDeniedPage("/noauth");}//==================
}

403处理器

package com.feng.securitydemo01.config;import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;@Component
public class MyAccessFail implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {//设置响应状态码response.setStatus(HttpServletResponse.SC_FORBIDDEN);//设置响应数据格式response.setContentType("application/json;charset=utf-8");//输入响应内容PrintWriter writer = response.getWriter();String json="{\"status\":\"403\",\"msg\":\"无权访问\"}";writer.write(json);writer.flush();}
}
package com.feng.securitydemo01.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {@AutowiredMyAccessFail myAccessFail;@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable()  //关闭csrf.formLogin()
//                .usernameParameter("自己根据情况写") 自定义表单的username,password
//                .passwordParameter("自己情况写")//还有rememberme、logout功能,极其简单,这里就不说了.loginProcessingUrl("/login")  //登录的url.successForwardUrl("/index")   //登录成功后走/index.and().authorizeRequests().antMatchers("/","/index","/toLogin").permitAll()  //"/","/index"放行.anyRequest().authenticated(); //所有请求都需要认证//配置403处理器,修改response中的内容,前后端分离,是根据响应体来具体判断的。//http.exceptionHandling().accessDeniedHandler(myAccessFail);//自定义403页面,前后端不分离http.exceptionHandling().accessDeniedPage("/noauth");}
}

源码

https://download.csdn.net/download/okok__TXF/80962384

-前后端分离

如果前文误人子弟太多了,就不写了,这篇也删了。。。根据情况来写吧,

SpringSecurity 入门相关推荐

  1. SpringSecurity入门(SSM版)

    1. 简介 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Sp ...

  2. SpringSecurity入门

    Spring Security介绍: Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架. (https://project ...

  3. SpringSecurity入门到入土教程_2 Oauth教程

    https://gitee.com/fakerlove/spring-security 文章目录 SpringOauth 教程 1. 简介 1.1 oauth2 概念 架构图 验证流程 spring ...

  4. SpringSecurity入门到入土教程_1

    https://gitee.com/fakerlove/spring-security 文章目录 SpringSecurity 教程 1. 简介 1.1 概念 1.2 入门案例 1.3 自定义登录逻辑 ...

  5. 普歌 - SpringSecurity入门(1)

    构建SpringSecurity模块化工程 前言 一.项目结构 二.使用maven创建项目 1.创建项目 parent的pom文件: base的pom文件: core的pom文件: web的pom文件 ...

  6. SpringSecurity入门01(含源码)

    环境:SpringBoot 2.1 + Mybatis + Spring Security 5.0 一.导入依赖 导入 spring-boot-starter-security 依赖,在 Spring ...

  7. SpringSecurity - 基础篇

    文章目录 一.SpringSecurity能做什么 二.SpringSecurity替代方案 三.权限管理中的相关概念 四.SpringSecurity 入门案例 前言:通常我们写http接口是不会用 ...

  8. .anonymous springsecurity需要登陆嘛_springSecurity之java配置篇

    一 前言 本篇是springSecurity知识的入门第二篇,主要内容是如何使用java配置的方式进行配置springSeciruty,然后通过一个简单的示例自定义登陆页面,覆盖原有springSec ...

  9. 学习SpringSecurity这一篇就够了

    目录 一.SpringSecurity 框架简介 1.1.概要 1.2.Spring Security到底能干什么? 1.3.常用术语 1.4.历史 1.5.同款产品对比 1.6.模块划分 二.Spr ...

最新文章

  1. 【CSS】【13】文字的排版
  2. java final
  3. 纯C#代码的Excel读取器(不需要Office Excel Com组件)
  4. redhat 6 配置 yum 源
  5. python字符串可以保存在变量中吗_在python中可以从字符串变量实例化类吗?
  6. 肺部胸片图像掩膜和伪彩色处理matlab
  7. (十一)深入浅出TCPIP之TCP粘包问题
  8. linux 64位内存划分,linux 64位内存空间大小?(linux 64 bits memory space size? [closed])
  9. css 矩形两边挖半圆
  10. Log4j2日志框架集成Slf4j日志门面
  11. (组合数学笔记)格点路径问题分析求解
  12. mysql linux 用户_Linux mysql添加用户,删除用户,以及用户权限
  13. oracle 主键自增
  14. 大学生计算机考试系统软件,我爱C”《大学计算机基础》考试系统学生端软件使用说明.doc...
  15. Android 动画 - TranslateAnimation位移动画
  16. 如何在word中的框中打钩、打叉
  17. 13丨性能测试场景:如何进行场景设计
  18. 1.计算机发展阶段 计算机发展历史 机械式计算机 机电式计算机 电子计算机 逻辑电路与计算机 二极管 电子管 晶体管 硅 门电路 计算机 电磁学计算机二进制...
  19. Monash call (莫纳什来电) -开篇
  20. 激光雷达:点云语义分割算法

热门文章

  1. GraphTCN: Spatio-Temporal Interaction Modeling for Human Trajectory Prediction
  2. 计算机数据库服务器名,附加数据库 对于 服务器“服务器计算机名”失败。 (Microsoft.SqlServer.Smo)...
  3. android 三星拍照,据说卖断货?我替大伙试试三星S10的拍照功能好不好用
  4. 手写最大堆(Java实现)
  5. 我们做互联网的产品的时候,面对不同的细分市场
  6. wordpress主题GIT,多功能高级WordPress主题
  7. C#中用“橡皮条”法绘图和重绘
  8. linux系统var日志磁盘满了,Linux 系统(Centos版本)磁盘空间占满的解决办法。
  9. 《动物园之星》给模拟经营游戏带来了什么
  10. 回车、换行、空格的ASCII码值—(附ASCII码表)