SpringBoot整合Shiro

2023-10-26

一、pom.xml引入依赖

1、shiro依赖

<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.9.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.9.0</version>
        </dependency>

2、缓存依赖(看需求)

<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.5.3</version>
        </dependency>

3、shiro注解权限控制依赖 及 shiro标签依赖(看需求)

<!--shiro注解权限控制-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!--shiro标签-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

二、yaml配置文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/rbac?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123
    type: com.alibaba.druid.pool.DruidDataSource
mybatis:
  typeAliasesPackage: com.wnxy.springbootshiro.entity
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    lazy-loading-enabled: true
    aggressive-lazy-loading: false
  mapper-locations: classpath:mapper/*.xml
server:
  servlet:
    session:
      tracking-modes: cookie

三、创建ehcache.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache >
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

四、应用程序入口类上添加注解@MapperScan,扫描mapper层

五、实现各层代码

mapper.xml文件

    <select id="selectSysUserByTel" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from sys_user
        where  telephone = #{tel}
    </select>
    <select id="selectRolesByTel" resultType="java.lang.String">
        select
            sr.rolename
        from sys_user left join sys_user_role sur on sys_user.id = sur.uid
                      left join sys_role sr on sr.id = sur.rid
        where  telephone = #{tel}
    </select>
    <select id="selectPersByTel" resultType="java.lang.String">
        select
            sp.NAME
        from sys_user left join sys_user_role sur on sys_user.id = sur.uid
                      left join sys_role_permission srp on sur.rid = srp.rid
                      left join sys_permission sp on sp.id = srp.perid
        where  telephone = #{tel}
    </select>

mapper接口

public interface SysUserMapper {
    public SysUser selectSysUserByTel(String tel);
    public Set<String> selectRolesByTel(String tel);
    public Set<String> selectPersByTel(String tel);

service。。。

controller

@Controller
@RequestMapping("user")
@Slf4j
public class UserController {
    @Autowired
    private SysUserService sysUserService;
    @GetMapping("loginDo")
    public String loginDo(String tel, String password,@RequestParam(value = "isRememberMe",defaultValue = "0") Integer rememberMe){
        try {
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(tel, password);
            //添加记住我功能
            if (rememberMe==1) {
                usernamePasswordToken.setRememberMe(true);
            }
            subject.login(usernamePasswordToken);
        //得到session.并把user存放到session中
            subject.getSession().setAttribute("user",subject.getPrincipal());
            log.info("是否有管理员角色:{}",subject.hasRole("管理员"));
            log.info("是否有用户管理权限:{}",subject.isPermitted("用户管理"));
            return "redirect:../admin";
        } catch (AuthenticationException e) {
            return "redirect:../login";
        }
    }
    @GetMapping("queryAll")
    @RequiresRoles("管理员")
    public String queryAll(@RequestParam(value = "userName",required = false)String userName, Model model){
        List<SysUser> sysUsers = sysUserService.queryAll(userName);
        model.addAttribute("list",sysUsers);
        return "user/show";

    }
    @PostMapping("addDo")
    public String addDo(SysUser sysUser){
        sysUser.setPassword(MD5Util.getMD5(sysUser.getPassword(),sysUser.getTelephone()));
        sysUserService.insertSelective(sysUser);
        return "redirect:queryAll";
    }

自定义realm

package com.wnxy.springbootshiro.config;

import com.wnxy.springbootshiro.entity.SysUser;
import com.wnxy.springbootshiro.service.SysUserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Set;

/**
 * @author Mrz
 * @date 2022/8/16 11:21
 */
public class MyRealm extends AuthorizingRealm {
    @Autowired
    private SysUserService service;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        Set<String> roles = service.selectRolesByTel(sysUser.getTelephone());
        Set<String> pers = service.selectPersByTel(sysUser.getTelephone());
        simpleAuthorizationInfo.setRoles(roles);
        simpleAuthorizationInfo.setStringPermissions(pers);
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String tel = (String) authenticationToken.getPrincipal();
        SysUser sysUser = service.selectSysUserByTel(tel);
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(sysUser, sysUser.getPassword(), getName());
        simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(sysUser.getTelephone()));
        return simpleAuthenticationInfo;
    }
}
解决session丢失的拦截器
package com.wnxy.springbootshiro.util;


import com.wnxy.springbootshiro.entity.SysUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author Mrz
 * @date 2022/8/16 19:08
 */
public class SessionInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Subject subject = SecurityUtils.getSubject();
        if (!subject.isAuthenticated()&&subject.isRemembered()) {
            Session session = subject.getSession();
            if (session.getAttribute("user")==null) {
                SysUser user = (SysUser) subject.getPrincipal();
                session.setAttribute("user",user);
            }
        }
        return true;
    }
}
自定义MyWebMvcConfigurer:
package com.wnxy.springbootshiro.config;

import com.wnxy.springbootshiro.util.SessionInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author Mrz
 * @date 2022/8/16 19:25
 */
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
//添加拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SessionInterceptor()).addPathPatterns("/**").excludePathPatterns("/bootstrap/**","/login","/user/loginDo");
    }
//添加视图控制
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("login").setViewName("login");
        registry.addViewController("admin").setViewName("admin");
        registry.addViewController("home").setViewName("home");
        registry.addViewController("add").setViewName("user/add");
    }
}
自定义ShiroConfig:
package com.wnxy.springbootshiro.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Mrz
 * @date 2022/8/16 11:32
 */
@Configuration
public class ShiroConfig {
//配置缓存管理
    @Bean
    public EhCacheManager getEhCacheManager(){
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return ehCacheManager;
    }
//配置认证匹配器
    @Bean
    public HashedCredentialsMatcher getHashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        hashedCredentialsMatcher.setHashIterations(1024);
        return hashedCredentialsMatcher;
    }
    @Bean
    public MyRealm getMyRealm(HashedCredentialsMatcher hashedCredentialsMatcher){
        MyRealm myRealm = new MyRealm();
//将认证匹配器绑定到自定义的realm中
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return myRealm;

    }
    @Bean
    public DefaultSecurityManager getSecurityManager(MyRealm myRealm,EhCacheManager ehCacheManager){
//注意:生成安全管理者,我们使用的是针对web服务的DefaultWebSecurityManager
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(myRealm);
//绑定缓存管理器
        defaultWebSecurityManager.setCacheManager(ehCacheManager);
        return defaultWebSecurityManager;
    }
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultSecurityManager defaultSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultSecurityManager);
//拦截后跳转的路径
        shiroFilterFactoryBean.setLoginUrl("login");
//身份认证失败,则跳转到登录页面的配置 没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。
//        shiroFilter.setSuccessUrl("");//登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此。
//        shiroFilter.setUnauthorizedUrl("");//没有权限默认跳转的页面
//        shiroFilter.setFilterChainDefinitions("");//filterChainDefinitions的配置顺序为自上而下,以最上面的为准


        //Shiro验证URL时,URL匹配成功便不再继续匹配查找(所以要注意配置文件中的URL顺序,尤其在使用通配符时)
        // 配置不会被拦截的链接 顺序判断
        Map<String,String> map = new HashMap<>();
//放行
        map.put("/login","anon");
        map.put("/user/loginDo","anon");
        map.put("/bootstrap/**","anon");
//退出登录
        map.put("/logout","logout");
//记住我
        map.put("/**","user");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
//shiro注解配置通知
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
//配置方言,实现shiro标签
    @Bean
    public ShiroDialect shiroDialect(){
        return  new ShiroDialect();
    }
}

实现shiro标签html页面要加入标记属性

<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

加密工具:

package com.wnxy.springbootshiro.util;

import org.apache.shiro.crypto.hash.SimpleHash;

/**
 * @author Mrz
 * @date 2022/8/16 12:04
 */
public class MD5Util {
    public static String getMD5(String source,String salt){
        SimpleHash md5 = new SimpleHash("MD5", source, salt, 1024);
        return md5.toHex();
    }
}

异常跳转页面:

package com.wnxy.springbootshiro.util;


import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.naming.AuthenticationException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author Mrz
 * @date 2022/8/16 19:38
 */
@Component
public class MyException implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (ex instanceof UnauthorizedException || ex instanceof AuthenticationException) {
            return new ModelAndView("errors/403");
        }
        return null;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot整合Shiro 的相关文章

  • Java:迭代 org.w3c.dom.Document 中所有元素的最有效方法?

    在 Java 中迭代所有 DOM 元素最有效的方法是什么 类似这样的东西 但是对于当前的每个 DOM 元素org w3c dom Document for Node childNode node getFirstChild childNod
  • F找出打开所有灯泡的最少开关数量

    我试图理解给出的问题here http qa geeksforgeeks org 4118 find the minimum number switches you have press turn all bulbs及其解决方案 问题指出
  • 错误消息的顺序不正确

    在我的 Spring MVC 验证中 错误消息的顺序随机变化 我希望消息的顺序与页面上显示的顺序相同 我的 AccountForm java 类如下所示 NotNull message Account name cannot be empt
  • JNLP 作为 HTML 页面中的 Applet

    我试图在 HTML 页面中运行 JNLP 但 java 插件不运行 JNLP 只运行 Applet 这是我的代码
  • 使用 jpos api 打包 SUBFIELDS

    我正在尝试编写一个主要的包和解压包ISO消息 using 初级职位框架 问题是我的消息包含一些子字段 例如field 48 并且我一直将其视为空值 下面是我的配置 主程序 Packager
  • 如何检查一个类没有参数构造函数

    Object obj new Object try obj getClass getConstructor catch SecurityException e e printStackTrace catch NoSuchMethodExce
  • 如何将 TreeViewer 单元格的一部分设为粗体?

    我目前想编写一个基于 JFace 的 Eclipse 编辑器TreeViewer 我添加了一个CellLabelProvider to the TreeViewer 如果我直接在update的方法CellLabelProvider对于粗体字
  • Hibernate 注释上分配的生成器类

    这里是休眠新手 我正在研究一个简单的 Hibernate 映射文件 当我使用 xml 方法时 我将生成器类设置为分配 在分配员工 ID 之前必须检查某些逻辑 因此我无法生成它 自动地
  • 实现移动运动体

    Project 我正在制作一个简单的项目 其中我希望能够制作一个运动体并将其从 x 点移动到 y 点 创建运动体 似乎没有太多直接内容 我一直在关注一些关于实现运动体的不同教程 因为我找不到指定如何正确执行此操作的教程 Problem 这似
  • UnknownFieldException - 没有这样的字段

    当我尝试使用 xstream 将 xml 文件编组到 POJO 中时 我不断收到此错误 我不确定发生了什么 可以用新的眼光来看待事物 Exception in thread main com thoughtworks xstream con
  • 使用 spring-data-rest 定义路径“/{resourcename}/search/”的自定义方法

    我很困惑 我无法找到如何将自定义 搜索 方法与在 spring data rest 的帮助下加载的方法一起定义 您能回答我吗 该框架是否具有 开箱即用 的可能性 如果有的话 你能告诉我 在哪里可以找到它吗 为了更深入地了解我的情况 我描述了
  • Android面向对象编程

    我正在使用 Eclipse 在 Android 中进行一些基本编程 我目前正在翻阅一本书并使用书中编写的一些示例代码 我注意到 在这本书中 迄今为止的所有示例都在主要活动中进行 我不认为这是很好的面向对象编程实践 因为我来自传统的 Java
  • 是否可以托管 Java Web 服务而不将其部署在 Tomcat/JBoss 等服务器上?

    据我所知 需要 Apache Tomcat 或像 JBoss 这样的应用服务器来部署和运行用 java 实现的 Web 服务 我的经理问我 是否有其他方法可以在不配置或设置 Apache Tomcat JBoss 的情况下部署 Web 服务
  • 如何从JTable中获取图标

    我已经更改了单元格渲染JTable使用以下代码显示图像而不是文本 base table getColumnModel getColumn 3 setCellRenderer new TableCellRenderer Override pu
  • 如何以 UTF-8 打开文件并以 UTF-16 写入另一个文件

    如何打开 UTF 8 格式的文件并写入 UTF 16 格式的另一个文件 我需要一个例子 因为我对 和 a 等某些字符有疑问 当写 m dic 时 我发现文件中写着 m dic 您可以按如下方式创建阅读器 InputStream is new
  • 使用||在开关的情况下?

    因此 对于 Java 基础知识的大学实验室来说 我遇到了麻烦 我必须设置一个开关 并在该开关内放置一个盒子 有3个选项供用户输入 每个选项都可以用字母来回答 问题是这个字母允许是大写或小写 问题是我似乎不知道如何设置它 所以一个案例将允许其
  • Spring Boot加载图片后上传

    我能够将图像上传到服务器 并且可以在路径中找到我的图像 static images gallery 现在 当我尝试加载上传的图像时 应用程序不显示主题 仅在应用程序重新启动后 我遇到了同样的问题 因为启动时加载了静态目录 上传路径一定要放在
  • String类如何重写+运算符?

    为什么在 Java 中 当 String 是一个类时 您可以使用 运算符添加字符串 在里面String java代码我没有找到这个运算符的任何实现 这个概念违反了面向对象吗 我们看一下Java中的以下简单表达式 int x 15 Strin
  • 使用 < 有什么区别? java 泛型中的 extends SomeAbstract> 与 SomeAbstract

    我将从 DotNet 转向 java 这种扩展的想法是新的 我看过一些帖子充分解释了使用List
  • 通过代码在LinearLayout中对齐Imageview

    问题很简单 我正在使用代码动态创建一个图像视图 ImageView btnSend new ImageView this 并将其添加到 LinearLayout 中 问题是我想保持右对齐 怎么做 提前致谢 尝试使用 LayoutParams

随机推荐

  • 一文看懂PCB助焊层跟阻焊层的区别与作用

    一文看懂PCB助焊层跟阻焊层的区别与作用 PCBworld 今天 阻焊层简介 阻焊盘就是soldermask 是指板子上要上绿油的部分 实际上这阻焊层使用的是负片输出 所以在阻焊层的形状映射到板子上以后 并不是上了绿油阻焊 反而是露出了铜皮
  • zookeeper 搭建集群

    待完善
  • 《计算机文化基础》22-23第一学期后十周教学计划(中国铁道出版社第三版)

    课程 任课教师 授课班级 编制时间 计算机文化基础 2022 10 28 授课日期 2022年 10月31日至 2022年 12月 16日 本课程总课时 28课时 已授课时 0 课时 尚余课时 28课时 本学期授课周 7周 本学期周课时 4
  • 超详细讲解!Android面试题集2021版,面试心得体会

    前言 Android常用知识体系是什么鬼 所谓常用知识体系 就是指对项目中重复使用率较高的功能点进行梳理 注意哦 不是Android知识体系 古语道 学而不思则罔 思而不学则殆 如果将做项目类比为 学 那么整理就可以类比为 思 在做项目过程
  • 文件包含漏洞

    一 文件包含函数 将外部文件的内容引入当前环境 include
  • 玩转Kali之初始化系统

    文章目录 下载镜像 安装系统 修改root密码 配置APT国内源 更新软件包 下载镜像 1 打开kali官网 https www kali org 安装系统 1 打开VirtualBox 2 选择新建虚拟机 1 输入虚拟机名称 2 选择安装
  • TopK问题的三种解法

    TopK问题是指从n个数据中取前K个数据 在生活中的应用也有很多 如游戏中xxx的排行榜前10名等 在这篇博客中我将主要利用堆去解决TopK问题 堆排序 首先我们需要建一个堆 然后我们再进行堆排序 排好序后直接取前K个就可以了 需要注意的是
  • Debian10iptables放行语法

    文章目录 1 基本语法 2 修改默认规则 3 实例 4 易错总结 1 基本语法 iptables A 链 匹配条件 j 动作 D 删除 p 协议 ACCEPT 放行 s 源ip地址 DROP 丢弃 d 目的ip地址 REJECT 拒绝 sp
  • java.util.EnumSet complementOf (EnumSet<E> s)方法具有什么功能呢?

    转自 java util EnumSet complementOf EnumSet lt E gt s 方法具有什么功能呢 下文笔者讲述EnumSet complementOf方法的功能简介说明 如下所示 EnumSet complemen
  • To Java程序员:切勿用普通for循环遍历LinkedList

    ArrayList与LinkedList的普通for循环遍历 对于大部分Java程序员朋友们来说 可能平时使用得最多的List就是ArrayList 对于ArrayList的遍历 一般用如下写法 public static void mai
  • 余弦定理实现新闻自动分类算法

    前言 余弦定理 这个在初中课本中就出现过的公式 恐怕没有人不知道的吧 但是另外一个概念 可能不是很多的人会听说过 他叫空间向量 一般用e表示 高中课本中有专门讲过这个东西 有了余弦定理和向量空间 我们就可以做许多有意思的事情了 利用余弦定理
  • Spring框架(三)Spring注解和获取Bean对象详解

    目录 一 什么是基于Java的Spring注解配置 具体注解的例子 二 更好的将Bean存储在Spring中 1 前置工作 在配置文件中设置Bean根路径 2 添加注解存储Bean对象 2 1 Controller 控制器存储 2 2 Se
  • Vue中 实现上一篇下一篇的功能

    效果 看下html页面 div class NewsDetails cont footer div img src assets img newsDetail 公共 更多2 1 png alt span 上一篇 lastTitle span
  • 软件开发 文档 质量

    1 在撰写API文档时 如果某个API的性能 时间性能 内存性能 特别低 应该在文档里详细列出 如此做的好处是 1 1 有助于客户在设计阶段 采用正确 高效的方案 1 2 对于开发这个API的team 可以减轻维护压力 明确责任 因为在文档
  • 中文同义句在线转换器 - 中文同义句转换器软件

    在线同义句转换器 中文同义句在线转换器 中文同义句转换器软件 made in Japan 祝你学习进步 更上一层楼 请记得采纳 谢谢 同义句转换器 1 I d like to go to the beach on vacation beca
  • 【1803. 统计异或值在范围内的数对有多少】

    来源 力扣 LeetCode 描述 给你一个整数数组 nums 下标 从 0 开始 计数 以及两个整数 low 和 high 请返回 漂亮数对 的数目 漂亮数对 是一个形如 i j 的数对 其中 0 lt i lt j lt nums le
  • 2019年黑马新版Java学习路线图(内含大纲+视频+工具+书籍+面试)面试必看!

    非常好的java学习路线 伴有配套资源 面试必看 黑马程序员 http bbs itheima com thread 386464 1 1 html
  • LEVELDB介绍

    基本信息 特性 keys 和 values 是任意的字节数组 数据按 key 值排序存储 调用者可以重载函数来重写排序顺序 提供基本的 Put key value Get key Delete key Batch 操作 多个更改可以在一个原
  • JWT

    1 常见的认证机制 1 1 HTTP Basic Auth HTTP Basic Auth简单点说明就是每次请求API时都提供用户的username和password 简言之 Basic Auth是配合RESTful API 使用的最简单的
  • SpringBoot整合Shiro

    一 pom xml引入依赖 1 shiro依赖