序: 
本文使用springboot+mybatis+SpringSecurity 实现数据库动态的管理用户、角色、权限管理

本文细分角色和权限,并将用户、角色、权限和资源均采用数据库存储,并且自定义滤器,代替原有的FilterSecurityInterceptor过滤器, 
并分别实现AccessDecisionManager、InvocationSecurityMetadataSourceService和UserDetailsService,并在配置文件中进行相应配置。

spring security的简单原理:

使用众多的拦截器对url拦截,以此来管理权限。但是这么多拦截器,笔者不可能对其一一来讲,主要讲里面核心流程的两个。

首先,权限管理离不开登陆验证的,所以登陆验证拦截器AuthenticationProcessingFilter要讲; 
还有就是对访问的资源管理吧,所以资源管理拦截器AbstractSecurityInterceptor要讲;

但拦截器里面的实现需要一些组件来实现,所以就有了AuthenticationManager、accessDecisionManager等组件来支撑。

现在先大概过一遍整个流程,用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。 
访问资源(即授权管理),访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面。


重要

本文设计和代码是基于 上一篇博客(请点击)

springboot+mybatis+SpringSecurity 实现用户角色数据库管理

进行修改。


本文目录: 
1:数据库表设计 
2:权限表的业务 
3:springSecurity 配置修改 
4:修改home.html 文件 
5:修改HomeController.java 文件 
6:测试检验

目录结构如下:


1:数据库表设计

由于本文增加了权限表所以本文的数据库表为5个分别是: 用户表、角色表、权限表、用户角色中间表、角色权限中间表

初始化数据

  1.  
    注意:Sys_permission 表的url通配符为两颗星,比如说 /user下的所有url,应该写成 /user/**;
  2.  
    权限的名字可以随意起名
  • insert into SYS_USER (id,username, password) values (1,'admin', 'admin');
    insert into SYS_USER (id,username, password) values (2,'abel', 'abel'); insert into SYS_ROLE(id,name) values(1,'ROLE_ADMIN');
    insert into SYS_ROLE(id,name) values(2,'ROLE_USER'); insert into SYS_ROLE_USER(SYS_USER_ID,ROLES_ID) values(1,1);
    insert into SYS_ROLE_USER(SYS_USER_ID,ROLES_ID) values(2,2); BEGIN;
    INSERT INTO `Sys_permission` VALUES ('1', 'ROLE_HOME', 'home', '/', null), ('2', 'ROLE_ADMIN', 'ABel', '/admin', null);
    COMMIT; BEGIN;
    INSERT INTO `Sys_permission_role` VALUES ('1', '1', '1'), ('2', '1', '2'), ('3', '2', '1');
    COMMIT;

      

2:权限表的业务代码

2.1 java bean

Permission.java

  1. package com.us.example.domain;
    
    /**
    * Created by yangyibo on 17/1/20.
    */
    public class Permission { private int id;
    //权限名称
    private String name; //权限描述
    private String descritpion; //授权链接
    private String url; //父节点id
    private int pid; public int getId() {
    return id;
    } public void setId(int id) {
    this.id = id;
    } public String getName() {
    return name;
    } public void setName(String name) {
    this.name = name;
    } public String getDescritpion() {
    return descritpion;
    } public void setDescritpion(String descritpion) {
    this.descritpion = descritpion;
    } public String getUrl() {
    return url;
    } public void setUrl(String url) {
    this.url = url;
    } public int getPid() {
    return pid;
    } public void setPid(int pid) {
    this.pid = pid;
    }
    }

      

2.2 dao 层

在 com.us.example.dao 包下新建PermissionDao.java 文件。

PermissionDao.java

  1. package com.us.example.dao;
    import com.us.example.config.MyBatisRepository;
    import com.us.example.domain.Permission;
    import java.util.List; /**
    * Created by yangyibo on 17/1/20.
    */
    public interface PermissionDao {
    public List<Permission> findAll();
    public List<Permission> findByAdminUserId(int userId);
    }

      

在src/resource/mapper目录下新建对应的mapper.xml 文件

PermissionDaoMapper.xml

  1. <?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.us.example.dao.PermissionDao">
    <select id="findAll" resultType="com.us.example.domain.Permission"> SELECT * from Sys_permission ;
    </select> <select id="findByAdminUserId" parameterType="int" resultType="com.us.example.domain.Permission">
    select p.*
    from Sys_User u
    LEFT JOIN sys_role_user sru on u.id= sru.Sys_User_id
    LEFT JOIN Sys_Role r on sru.Sys_Role_id=r.id
    LEFT JOIN Sys_permission_role spr on spr.role_id=r.id
    LEFT JOIN Sys_permission p on p.id =spr.permission_id
    where u.id=#{userId}
    </select>
    </mapper>

3:springSecurity 配置修改

3.1 修改 WebSecurityConfig.java

修改com.us.example.config包下的 WebSecurityConfig.java 文件如下:

  1. package com.us.example.config;
    
    import com.us.example.service.CustomUserService;
    import com.us.example.service.MyFilterSecurityInterceptor;
    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.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; /**
    * Created by yangyibo on 17/1/18.
    */ @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
    private MyFilterSecurityInterceptor myFilterSecurityInterceptor; @Bean
    UserDetailsService customUserService(){ //注册UserDetailsService 的bean
    return new CustomUserService();
    } @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(customUserService()); //user Details Service验证 } @Override
    protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
    .anyRequest().authenticated() //任何请求,登录后可以访问
    .and()
    .formLogin()
    .loginPage("/login")
    .failureUrl("/login?error")
    .permitAll() //登录页面用户任意访问
    .and()
    .logout().permitAll(); //注销行为任意访问
    http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
    }
    }

3.2 修改CustomUserService

修改CustomUserService.java 内容如下:

  1. package com.us.example.service;
    
    import com.us.example.dao.PermissionDao;
    import com.us.example.dao.UserDao;
    import com.us.example.domain.Permission;
    import com.us.example.domain.SysRole;
    import com.us.example.domain.SysUser;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.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 java.util.ArrayList;
    import java.util.List; /**
    * Created by yangyibo on 17/1/18.
    */
    @Service
    public class CustomUserService implements UserDetailsService { //自定义UserDetailsService 接口 @Autowired
    UserDao userDao;
    @Autowired
    PermissionDao permissionDao; public UserDetails loadUserByUsername(String username) {
    SysUser user = userDao.findByUserName(username);
    if (user != null) {
    List<Permission> permissions = permissionDao.findByAdminUserId(user.getId());
    List<GrantedAuthority> grantedAuthorities = new ArrayList <>();
    for (Permission permission : permissions) {
    if (permission != null && permission.getName()!=null) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getName());
    //1:此处将权限信息添加到 GrantedAuthority 对象中,在后面进行全权限验证时会使用GrantedAuthority 对象。
    grantedAuthorities.add(grantedAuthority);
    }
    }
    return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
    } else {
    throw new UsernameNotFoundException("admin: " + username + " do not exist!");
    }
    } }

      

3.3 新增MyAccessDecisionManager

在com.us.example.service 包下新增 
MyAccessDecisionManager.java 文件

  1. package com.us.example.service;
    
    import org.springframework.security.access.AccessDecisionManager;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.authentication.InsufficientAuthenticationException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.stereotype.Service; import java.util.Collection;
    import java.util.Iterator; /**
    * Created by yangyibo on 17/1/19.
    */
    @Service
    public class MyAccessDecisionManager implements AccessDecisionManager { // decide 方法是判定是否拥有权限的决策方法,
    //authentication 是释CustomUserService中循环添加到 GrantedAuthority 对象中的权限信息集合.
    //object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
    //configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if(null== configAttributes || configAttributes.size() <=0) {
    return;
    }
    ConfigAttribute c;
    String needRole;
    for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
    c = iter.next();
    needRole = c.getAttribute();
    for(GrantedAuthority ga : authentication.getAuthorities()) {//authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
    if(needRole.trim().equals(ga.getAuthority())) {
    return;
    }
    }
    }
    throw new AccessDeniedException("no right");
    } @Override
    public boolean supports(ConfigAttribute attribute) {
    return true;
    } @Override
    public boolean supports(Class<?> clazz) {
    return true;
    }
    }

3.4 新增 MyFilterSecurityInterceptor

在com.us.example.service 包下新增 
MyFilterSecurityInterceptor.java 文件

  1. package com.us.example.service;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.access.SecurityMetadataSource;
    import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
    import org.springframework.security.access.intercept.InterceptorStatusToken;
    import org.springframework.security.web.FilterInvocation;
    import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    import org.springframework.stereotype.Service; import java.io.IOException; /**
    * Created by yangyibo on 17/1/19.
    */
    @Service
    public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter { @Autowired
    private FilterInvocationSecurityMetadataSource securityMetadataSource; @Autowired
    public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
    super.setAccessDecisionManager(myAccessDecisionManager);
    } @Override
    public void init(FilterConfig filterConfig) throws ServletException { } @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain);
    invoke(fi);
    } public void invoke(FilterInvocation fi) throws IOException, ServletException {
    //fi里面有一个被拦截的url
    //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
    //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
    InterceptorStatusToken token = super.beforeInvocation(fi);
    try {
    //执行下一个拦截器
    fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
    } finally {
    super.afterInvocation(token, null);
    }
    } @Override
    public void destroy() { } @Override
    public Class<?> getSecureObjectClass() {
    return FilterInvocation.class;
    } @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
    return this.securityMetadataSource;
    }
    }

3.5 新增 MyInvocationSecurityMetadataSourceService

在com.us.example.service 包下新增MyInvocationSecurityMetadataSourceService.java文件

  1. package com.us.example.service;
    
    import com.us.example.dao.PermissionDao;
    import com.us.example.domain.Permission;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.access.SecurityConfig;
    import org.springframework.security.web.FilterInvocation;
    import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
    import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest;
    import java.util.*; /**
    * Created by yangyibo on 17/1/19.
    */
    @Service
    public class MyInvocationSecurityMetadataSourceService implements
    FilterInvocationSecurityMetadataSource { @Autowired
    private PermissionDao permissionDao; private HashMap<String, Collection<ConfigAttribute>> map =null; /**
    * 加载权限表中所有权限
    */
    public void loadResourceDefine(){
    map = new HashMap<>();
    Collection<ConfigAttribute> array;
    ConfigAttribute cfg;
    List<Permission> permissions = permissionDao.findAll();
    for(Permission permission : permissions) {
    array = new ArrayList<>();
    cfg = new SecurityConfig(permission.getName());
    //此处只添加了用户的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
    array.add(cfg);
    //用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
    map.put(permission.getUrl(), array);
    } } //此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
    if(map ==null) loadResourceDefine();
    //object 中包含用户请求的request 信息
    HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
    AntPathRequestMatcher matcher;
    String resUrl;
    for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
    resUrl = iter.next();
    matcher = new AntPathRequestMatcher(resUrl);
    if(matcher.matches(request)) {
    return map.get(resUrl);
    }
    }
    return null;
    } @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
    return null;
    } @Override
    public boolean supports(Class<?> clazz) {
    return true;
    }
    }

4:修改home.html 文件

修改src/resources/templates目录下 的home.html

  1. <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org"
    xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
    <head>
    <meta content="text/html;charset=UTF-8"/>
    <title sec:authentication="name"></title>
    <link rel="stylesheet" th:href="@{css/bootstrap.min.css}" />
    <style type="text/css">
    body {
    padding-top: 50px;
    }
    .starter-template {
    padding: 40px 15px;
    text-align: center;
    }
    </style>
    </head>
    <body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
    <div class="navbar-header">
    <a class="navbar-brand" href="#">Spring Security演示</a>
    </div>
    <div id="navbar" class="collapse navbar-collapse">
    <ul class="nav navbar-nav">
    <li><a th:href="@{/}"> 首页 </a></li>
    <li><a th:href="@{/admin}"> admin </a></li>
    </ul>
    </div><!--/.nav-collapse -->
    </div>
    </nav> <div class="container"> <div class="starter-template">
    <h1 th:text="${msg.title}"></h1> <p class="bg-primary" th:text="${msg.content}"></p> <div sec:authorize="hasRole('ROLE_HOME')"> <!-- 用户类型为ROLE_ADMIN 显示 -->
    <p class="bg-info" th:text="${msg.etraInfo}"></p>
    </div>
    <div sec:authorize="hasRole('ROLE_ADMIN')"> <!-- 用户类型为ROLE_ADMIN 显示 -->
    <p class="bg-info">恭喜您,您有 ROLE_ADMIN 权限 </p>
    </div> <form th:action="@{/logout}" method="post">
    <input type="submit" class="btn btn-primary" value="注销"/>
    </form>
    </div> </div> </body>
    </html>

5:修改HomeController.java 文件

  1. package com.us.example.controller;
    
    import com.us.example.domain.Msg;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody; /**
    * Created by yangyibo on 17/1/18.
    */
    @Controller
    public class HomeController { @RequestMapping("/")
    public String index(Model model){
    Msg msg = new Msg("测试标题","测试内容","欢迎来到HOME页面,您拥有 ROLE_HOME 权限");
    model.addAttribute("msg", msg);
    return "home";
    }
    @RequestMapping("/admin")
    @ResponseBody
    public String hello(){
    return "hello admin";
    }
    }

6.测试检验

启动访问 http://localhost:8080/ 到登录页面

  1.  
    由于数据库的配置 admin 用户拥有 访问 home和admin 页面的权限。
  2.  
    abel 用户只有访问 home 的权限
  • 1
  • 2
  • 3

使用admin 登录

点击 admin 按钮 会反回结果 “hello admin“

使用abel 用户登录 点击 点击 admin 按钮 页面会报403

源码地址:https://github.com/527515025/springBoot

参考资料: 
http://www.tuicool.com/articles/jq6fuur#c-23220 
http://blog.csdn.net/u012367513/article/details/38866465

springBoot+springSecurity 数据库动态管理用户、角色、权限(二)的更多相关文章

  1. springBoot+springSecurity 数据库动态管理用户、角色、权限

    使用spring Security3的四种方法概述 那么在Spring Security3的使用中,有4种方法: 一种是全部利用配置文件,将用户.权限.资源(url)硬编码在xml文件中,已经实现过, ...

  2. SpringBoot学习- 10、设计用户角色权限表

    SpringBoot学习足迹 前几节已经基本了解了SpringBoot框架常用的技术,其他的消息队列,定时器等技术暂时用不到,真正项目中如果基于微信系,阿里系开发的话,还要了解平台专用的技术知识,学习 ...

  3. [.Net MVC] 用户角色权限管理_使用CLK.AspNet.Identity

    项目:后台管理平台 意义:一个完整的管理平台需要提供用户注册.登录等功能,以及认证和授权功能. 一.为何使用CLK.AspNet.Identity 首先简要说明所采取的权限控制方式.这里采用了基于角色 ...

  4. ASP.NET MVC+EF框架+EasyUI实现权限管理系列(21)-用户角色权限基本的实现说明

    原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(21)-用户角色权限基本的实现说明     ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇)   (1):框 ...

  5. java权限管理与用户角色权限设计

    java权限管理与用户角色权限设计 实现业务系统中的用户权限管理 B/S系统中的权限比C/S中的更显的重要,C/S系统因为具有特殊的客户端,所以访问用户的权限检测可以通过客户端实现或通过客户端+服务器 ...

  6. Asp.Net MVC+BootStrap+EF6.0实现简单的用户角色权限管理

    这是本人第一次写,写的不好的地方还忘包含.写这个的主要原因是想通过这个来学习下EF的CodeFirst模式,本来也想用AngularJs来玩玩的,但是自己只会普通的绑定,对指令这些不是很熟悉,所以就基 ...

  7. Asp.Net MVC+BootStrap+EF6.0实现简单的用户角色权限管理10

    今天把用户的菜单显示和页面的按钮显示都做好了,下面先来个效果图 接下来说下我实现的方法: 首先我在每个方法前面都加了这个属性, /// <summary> /// 表示当前Action请求 ...

  8. spring-boot-plus V1.4.0发布 集成用户角色权限部门管理

    RBAC用户角色权限 用户角色权限部门管理核心接口介绍 Shiro权限配置

  9. spring security中动态更新用户的权限

    在程序的执行过程中,有时有这么一种需求,需要动态的更新某些角色的权限或某些人对应的权限,当前在线的用户拥有这个角色或拥有这个权限时,在不退出系统的情况下,需要动态的改变的他所拥有的权限. 需求:张三 ...

随机推荐

  1. pgsql物理复制(pgsql 备库的搭建以及角色互换,提升)

    结构图如下: Postgresql早在9.0版本开始支持物理复制,也称为流复制,通过从实例级复制出一个与主库一模一样的备库.流复制同步方式有同步,异步两种,如果主节点和备节点不是很忙,通常异步模式下备 ...

  2. Jquery复习(四)之text()、html()、val()

    三个简单实用的用于 DOM 操作的 jQuery 方法: text() - 设置或返回所选元素的文本内容 html() - 设置或返回所选元素的内容(包括 HTML 标记) val() - 设置或返回 ...

  3. npm学习(二)之如何防止权限错误

    如何防止权限错误 如果您在尝试全局安装包时看到EACCES错误,请阅读本章.如果更改安装npm的目录,通常可以避免此错误.要做到这一点,要么使用版本管理器重新安装npm(推荐)或手动更改npm的默认目 ...

  4. 安装linux mint后要做20件事

    Linux Mint 17 Qiana Cinnamon Linux Mint 17已经发布,定名为Qiana.Mint是Linux最佳发行版之一,它定位于桌面用户,关注可用性和简洁.它携带了风格迥异 ...

  5. GDAL支持中文路径和Shp文件中文属性写入

    在使用GDAL的过程中,为了支持中文,比需手动进行中文路径的设置,同时特别是在对Shp的属性进行中文输入的时候,都必须进行必要的设定. 为了支持中文路径,在注册了驱动之后,加上第三句就可以了.必须设置 ...

  6. install - 复制文件并设置属性

    SYNOPSIS[总览] install [options] [-s] [--strip] source dest install [options] [-s] [--strip] source... ...

  7. jumpserver部署1.0版本

    A. jumpserver概述 跳板机概述: 跳板机就是一台服务器,开发或运维人员在维护过程中首先要统一登录到这台服务器,然后再登录到目标设备进行维护和操作: 跳板机缺点:没有实现对运维人员操作行为的 ...

  8. Node.js之querystring模块

    querystring从字面上的意思就是查询字符串,一般是对http请求所带的数据进行解析.querystring模块只提供4个方法,在我看来,这4个方法是相对应的. 这4个方法分别是querystr ...

  9. 对ECMAScript的研究-----------引用

    ECMAScript 新特性与标准提案 一:ES 模块 第一个要介绍的 ES 模块,由于历史上 JavaScript 没有提供模块系统,在远古时期我们常用多个 script 标签将代码进行人工隔离.但 ...

  10. Linux基础教程 linux无密码ssh登录设置

      概述 在一些常用设备之间ssh, scp,不用输入密码可以节省不少时间. 生成密钥 先看本地是否有密钥,如果有,则不用生成,否则会影响到以前打通的设备. 复制代码代码如下: 没有则用 ssh-ke ...