SpringBoot-Shiro的使用

2023-10-30

一、什么是Shiro

权限体系在现代软件应用中有着非常重要的地位。一个应用如果没有权限体系都会显着这个系统“特别不安全”,无论是传统的MIS系统还是互联网项目出于对业务数据和应用自身的安全,都会设置自己的安全策略。
目前市场上专门的Java权限框架有Apache Shiro 和 Spring Security。相较于Spring Security 来说 Shiro更加老牌。学习好Shiro对于以后市场上在出现新型权限框架的学习能带来很大便利。因为权限的概念是不变的,变得是框架的实现方式。当然了,对于第一次学习权限框架的人来说,相较于权限框架的应用,更难的就是权限方面的概念。

(一)核心功能

  • Authentication 认证。主要用于处理用户的登录并且做认证。
  • Authorization 授权。用户是否有权限访问指定URL等。
  • Cryptography 密码学。如密码的加密。
  • Session Management Session 管理。
  • Web Integration Web集成。Shiro不依赖于容器。

(二) shiro架构

2 Subject

主体。每个用户登录成功后都会对应一个Subject对象,所有用户信息都存放在Subject中。可以理解:Subject就是Shiro提供的用户实体类。

3 Security Manager

Shiro最大的容器,此容器中包含了Shiro的绝大多数功能。在非Spring Boot项目中,获取Security Manager 是编写代码的第一步。而在Spring Boot中已经帮助我们自动化配置了。

4 Authenticator

认证器。执行认证过程调用的组件。里面包含了认证策略。

5 Authorizer

授权器。执行授权时调用的组件。

6 Session Manager

Shiro被Web集成后,HttpSession对象会由Shiro的Session Manager进行管理。

7 Cache Manager

缓存管理。Shiro执行很多第三方缓存技术。例如:EHCache等。

8 Session DAO

操作Session内容的组件。

9 Realms

Shiro框架实现权限控制不依赖于数据库,通过内置数据也可以实现权限控制。但是目前绝大多数应用的数据都存储在数据库中,所以Shiro提供了Realms组件,此组件的作用就是访问数据库。Shiro内置的访问数据库的代码,通过简单配置就可以访问数据库,也可以自定义Realms实现访问数据库逻辑(绝大多数都这么做)

理解这张图片
在这里插入图片描述

(三) shiro的全局配置文件

shiro的全局配置文件是ini格式的,ini中放置数据,比如用户、角色、权限等,由于我们会将这些数据放在数据库中而不是固定在ini文件中所以本篇文章不详细介绍全局配置文件了。

(四)shiro的过滤器

理解shiro的过滤器是理解shiro的重中之重,shiro就是凭借这些过滤器来做权限或认证拦截的。

  1. anon:不认证也可以访问。例如:/admin/**=anon /login=anon
  2. authc:必须认证。 /**=authc —所有的资源都认证
  3. authcBasic:没有参数时表示httpBasic认证(客户端认证方式)。
  4. logout:退出。
  5. noSessionCreation:新增Filter,表示没有Session创建。
  6. perms:判断是有具有指定权限。例如:/admin/user/**=perms[“per1”,”per2”]。必须同时具有给定权限才可以访问。如果只有一个权限可以省略双引号。
  7. port:限制端口。例如:/admin/**=port[8081]。只要请求不是8081端口就重新发送URL到8081端口。
  8. rest:请求方式和权限的简便写法。例如:/admin/**=rest[user],相当于/admin/** = perms[user:方式],方式是http请求的方式:post、get等。
  9. roles:判断是否具有指定角色。/admin/**=roles[role1]
  10. ssl:表示是安全的请求。协议为https
  11. user:表示必须存在用户。

二、shiro认证和授权以及自定义配置

在说这两个核心功能之前我想再次强调一件事:在springboot中使用shiro并不需要我们手动去配置securityManager对象,我们通常会为shiro做自定义配置,让项目启动时就将securityManager对象创建好,我们只需要用就可以了

(一)认证

在这里插入图片描述
我们一般是通过subject来使用shiro的功能的,比如在登录中我们需要为用户做认证,我们需要为用户名和密码创建一个userNamePasswordtoken对象,再将这个token对象传到subject的login方法中,shiro会为我们做完剩下的数据校验以及认证工作

 @RequestMapping("userLogin")
    public String userLogin(String uname,String pwd,@RequestParam(defaultValue = "false") Boolean rm){
        //1.创建token对象
        UsernamePasswordToken token = new UsernamePasswordToken(uname, pwd,rm);
        //2.获取subject对象
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        session.setAttribute("user",uname);
        //3.执行登录
        subject.login(token);
        //4.页面跳转到main
        return "main";
    }

这时候我们会产生疑问?shiro要做数据比对首先得有我们的真实数据才行啊,怎么可能只通过用户输入的用户名和密码就完成登录校验的功能呢?没错,我们还需要增加一点配置才能完成我们的登录功能。

Realm登场

Realm就是shiro中获取数据的模块,默认realm是从shiro的全局配置文件中获取数据,但是在项目中我们会将数据放在数据库中,为了实现登录功能,我们需要重写realm,让shiro从数据库中拿到用户数据。

@Component
public class MyRealm extends AuthorizingRealm {
    @Autowired
    UserService service;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
     return null;
    }

    /**
     * 情景:当用户进行登录认证的时候shiro底层会使用该方法获取认证信息,但shiro默认向shiro.ini文件获取
     * 目的:使得shiro向数据库获取认证信息,并且做一些自定义操作,比如设置加密的盐等等
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.获取用户的身份信息
        String uname = authenticationToken.getPrincipal().toString();
        //2.根据用户名在数据库中查找用户
        User user = service.selUserService(uname);
        //3.判断用户是否存在
        if(user != null){
            //得到数据库密码
            String pwd = user.getPwd();
            //将身份信息与数据库中的密码还有盐存放到认证信息中以便于shiro的认证模块做比对
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(authenticationToken.getPrincipal(), pwd, ByteSource.Util.bytes("codexie"), uname);
            return info;
        }
        return null;
    }
}

(二)授权

在这里插入图片描述

授权分为两个部分,第一个部分是权限信息的获取,shiro需要知道这个登录的用户具备哪些权限,第二部分是权限认证,一般是通过在单元方法上加注解来表示这个单元方法只能被具备特定角色或权限的用户访问。
权限信息的获取
根据之前对realm的描述我们可以知道权限信息的获取也是通过自定义realm来实现

@Component
public class MyRealm extends AuthorizingRealm {
    @Autowired
    UserService service;

    /**
    *  情景:当用户进行权限认证的时候shiro底层会使用该方法获取认证信息,但shiro默认向shiro.ini文件获取
     * 目的:向数据库中选取该用户的权限信息,若没使用缓冲池则每次授权操作都需要执行一次该方法
     * @param principalCollection
     * @return AuthorizationInfo 授权信息对象
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //1.获取身份信息
        String pricipal = principalCollection.getPrimaryPrincipal().toString();

        //2.根据身份获取、角色权限等信息
        List<String> roles = service.getRolesService(pricipal);
        List<String> permissions = service.getPermissionsService(pricipal);

        //3.将角色、权限添加到授权信息里面
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roles);
        info.addStringPermissions(permissions);
        return info;
    }

    /**
     * 情景:当用户进行登录认证的时候shiro底层会使用该方法获取认证信息,但shiro默认向shiro.ini文件获取
     * 目的:使得shiro向数据库获取认证信息,并且做一些自定义操作,比如设置加密的盐等等
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.获取用户的身份信息
        String uname = authenticationToken.getPrincipal().toString();
        //2.根据用户名查找用户
        User user = service.selUserService(uname);
        //3.判断用户是否存在
        if(user != null){
            //得到数据库密码
            String pwd = user.getPwd();
            //将身份信息与数据库中的密码还有盐存放到认证信息中以便于shiro的认证模块做比对
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(authenticationToken.getPrincipal(), pwd, ByteSource.Util.bytes("codexie"), uname);
            return info;
        }
        return null;
    }
}

权限认证

 @RequestMapping("psCheck")
    @RequiresPermissions({"user:add","user:delete","admin:delete"})
    @ResponseBody
    public String psCheck(){
        return "权限认证成功";
    }

    @RequestMapping("adminCheck")
    @RequiresRoles("admin")
    @ResponseBody
    public String adminCheck(){
        return "角色认证成功";
    }

常用注解

  • @RequiresAuthentication
    验证用户是否登录,等同于方法subject.isAuthenticated() 结果为true时。
  • @RequiresUser
    验证用户是否被记忆,user有两种含义:
    一种是成功登录的(subject.isAuthenticated() 结果为true);
    另外一种是被记忆的(subject.isRemembered()结果为true)。
  • @RequiresGuest
    验证是否是一个guest的请求,与@RequiresUser完全相反。
    换言之,RequiresUser == !RequiresGuest。
    此时subject.getPrincipal() 结果为null.也就是所谓的游客
  • @RequiresRoles
    例如:@RequiresRoles(“aRoleName”);
    void someMethod();
    如果subject中有aRoleName角色才可以访问方法someMethod。如果没有这个权限则会抛出异常AuthorizationException。
  • @RequiresPermissions
    例如: @RequiresPermissions({“file:read”, “write:aFile.txt”} )
    void someMethod();
    要求subject中必须同时含有file:read和write:aFile.txt的权限才能执行方法someMethod()。否则抛出异常AuthorizationException

shiro整合Ehcache

我们在做权限认证的时候,每做一次权限认证就需要获取一次权限信息(也就是走数据库),这样是非常消耗资源并且用户体验不好的,我们可以在第一次获取完用户的权限信息,若用户在规定时间内再做授权认证时直接从缓冲池里拿数据而不是从数据库中拿。Ehcache就是一个非常方便我们使用的缓冲池。

Ehcache简介

EHCache是sourceforge的开源缓存项目,现已经具有独立官网,网址:(http://www.ehcache.org)。其本身是纯JAVA实现的,所以可以和绝大多数Java项目无缝整合,例如:Hibernate的缓存就是基于EHCache实现的。
EHCache支持内存和磁盘缓存,默认是存储在内存中的,当内存不够时允许把缓存数据同步到磁盘中,所以不需要担心内存不够问题。
EHCache支持基于Filter的Cache实现,同时也支持Gzip压缩算法提高响应速度。
ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便。

Ehcache的使用

属性含义:
maxElementsInMemory:缓存中允许创建的最大对象数。
eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。
timeToIdleSeconds:缓存数据的钝化时间,取值0表示无限长。
timeToLiveSeconds:缓存数据的生存时间,取值0表示无限长。
overflowToDisk:内存不足时,是否启用磁盘缓存。
memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="ehcache" updateCheck="false">

    <!-- 磁盘缓存位置 -->
    <diskStore path="java.io.tmpdir"/>
    <!-- 默认缓存 -->
    <defaultCache
            maxEntriesLocalHeap="1000"
            eternal="false"
            timeToIdleSeconds="3600"
            timeToLiveSeconds="3600"
            overflowToDisk="false">
    </defaultCache>

    <!-- 记录用户的授权信息的缓存:缓存用户的角色和权限 -->
    <cache name="passwordRetryEhcache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="60"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

</ehcache>

api演示

public static void main(String[] args) {
  /*  String property = System.getProperty("java.io.tmpdir");
    System.out.println(property);*/
    //根据配置文件创建cacheManager对象
    CacheManager  cacheManager=CacheManager.create("D:\\SXTCourse\\Testshiro\\uu\\src\\main\\resources\\each.xml");
    //根据缓冲池名字获取缓冲池
    Cache cache = cacheManager.getCache("HelloWorldCache");
    //创建一个缓存对象
    Element  element=new Element("key","sxt");
    //将该对象放入缓冲池
    cache.put(element);
    //通过键名向缓冲池中取对象
   Element element1 = cache.get("key");
    System.out.println(element1);
}

将Ehcache整合到shiro

我们只用在配置securityManager的bean的时候设置ehcacheManager为securityManager的cacheManager就可以了
代码片段

 /**得到一个securityManager对象,该对象是全局单例的
     * 我们要为它设置自定义realm模块、rememberMeManager模块
     * @return
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(){
        //1.创建securManager对象
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        //2.创建matcher
        myMatcher.setHashAlgorithmName("md5");
        myMatcher.setHashIterations(3);
        //3.将matcher设置到realm中
        myRealm.setCredentialsMatcher(myMatcher);


        //4.集中设置securityManager
        securityManager.setRealm(myRealm);
        securityManager.setRememberMeManager(rememberMeManager());
        securityManager.setCacheManager( ehCacheCacheManager());
        return securityManager;
    }

    @Bean
    public EhCacheManager ehCacheCacheManager(){
        //1.创建ehCacheManager
        EhCacheManager ehCacheManager = new EhCacheManager();

        //2.读取配置文件
        InputStream in = null;
        try {
            in = ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml");

        } catch (IOException e) {
            e.printStackTrace();
        }
        //3.根据配置文件创建cacheManager对象
        net.sf.ehcache.CacheManager cacheManager = new net.sf.ehcache.CacheManager(in);
        //4.将cacheManager对象传给ehCacheManager对象
        ehCacheManager.setCacheManager(cacheManager);

        return ehCacheManager;

    }

配置完后,shiro默认就会从我们的默认缓冲池中拿权限信息,如果没有则会调用service拿取数据

自定义配置matcher

(一)matcher使用的时机

matcher是在用户执行登录(认证操作)的时候,shiro的认证模块会调用realm中matcher的doCredentialsMatch方法做信息比对,通过该方法返回的布尔值来决定认证是否成功

使用场景

现在有如下场景,当我们登录失败时服务器要记住用户登录失败的次数,若用户连续登录失败三次,则告知用户账号被锁定十分钟,若用户在三次内输入正确密码则清空之前的失败次数。
思路:我们可以使用缓冲池解决此问题,若用户登录失败则将用户名作为键,失败次数作为值放入缓冲池中,若用户连续输错则将值+1,当值大于等于3的时候抛出异常告诉用户账号被锁定。

/**
 * 该类的作用是对HashedCredentialsMatcher的功能进行封装,采用静态代理的设计模式
 * 具体功能:若用户在十分钟内连续输错三次则用户将被锁定十分钟
 * 思路:采用ehCacheManager的特点,设置一个数据钝化时间为10分钟的缓冲池
 *      当用户登录时判断用户的输错次数是否大于等于3,若是则不用进行数据库的比对,直接抛出异常
 *      若不是则跟数据库进行比对,比对成功则清空之前的记录,比对失败则将失败次数+1
 */
@Component
public class MyMatcher extends HashedCredentialsMatcher {
    private Ehcache passwordEhcache;

    public MyMatcher(EhCacheManager ehCacheCacheManager) {
        Ehcache passwordRetryEhcache =  ehCacheCacheManager.getCacheManager().getEhcache("passwordRetryEhcache");
        passwordEhcache =  passwordRetryEhcache;
    }

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        int i = 0;
        //1.得到uname
        String uname = token.getPrincipal().toString();
        //2.根据uname得到缓冲池中的密码
        Element element = passwordEhcache.get(uname);

        //3.若该用户名对应的元素为空则将其初始化并放入缓冲池
        if(element == null){
            element = new Element(uname,0);
            passwordEhcache.put(element);
        }else{ //4.否则比较该元素的值是否大于等于3,若是则抛异常
            if((Integer) element.getObjectValue() >= 3){
                throw new ExcessiveAttemptsException();
            }
        }

        //5.调用父级方法做信息核验
        boolean match = super.doCredentialsMatch(token, info);
        //6.若核验成功则移除之前的记录
        if(match){
            passwordEhcache.remove(uname);
        }else{
            //否则将失败记录+1
            Integer integer = (Integer) element.getObjectValue();
            integer++;

            passwordEhcache.put(new Element(uname,integer));
            System.out.println("失败次数:"+integer);
        }
        return match;
    }
}

shiro在springboot中的配置

@Configuration
public class ShiroConfig {
    @Autowired
    MyRealm myRealm;

    @Autowired
    MyMatcher myMatcher;

    /**得到一个securityManager对象,该对象是全局单例的
     * 我们要为它设置自定义realm模块、rememberMeManager模块
     * @return
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(){
        //1.创建securManager对象
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        //2.创建matcher
        myMatcher.setHashAlgorithmName("md5");
        myMatcher.setHashIterations(3);
        //3.将matcher设置到realm中
        myRealm.setCredentialsMatcher(myMatcher);


        //4.集中设置securityManager
        securityManager.setRealm(myRealm);
        securityManager.setRememberMeManager(rememberMeManager());
        securityManager.setCacheManager( ehCacheCacheManager());
        return securityManager;
    }

    @Bean
    public EhCacheManager ehCacheCacheManager(){
        //1.创建ehCacheManager
        EhCacheManager ehCacheManager = new EhCacheManager();

        //2.读取配置文件
        InputStream in = null;
        try {
            in = ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml");

        } catch (IOException e) {
            e.printStackTrace();
        }
        //3.根据配置文件创建cacheManager对象
        net.sf.ehcache.CacheManager cacheManager = new net.sf.ehcache.CacheManager(in);
        //4.将cacheManager对象传给ehCacheManager对象
        ehCacheManager.setCacheManager(cacheManager);

        return ehCacheManager;

    }

    /**
     * 目的:为Shiro的rememberMe模块设置自定义cookie
     * 主要是设置cookie的范围、存在时间等
     * @return
     */
    public SimpleCookie myCookie(){
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(60*60*24*10);
        cookie.setPath("/");
        return cookie;
    }

    /**
     * 创建CookieRememberMeManager对象,将自定义cookie传给它
     * @return
     */
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        rememberMeManager.setCookie(myCookie());
        rememberMeManager.setCipherKey("1234567890987654".getBytes());
        return rememberMeManager;
    }

    /**
     * 初始化shiro各权限过滤器的范围
     * @return
     */
    @Bean
    public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        //将登录页面的请求设置成不需要认证
        chainDefinition.addPathDefinition("/userController/login","anon");
        //将用户登录的请求设置成不需要认证
        chainDefinition.addPathDefinition("/userController/userLogin","anon");
        //设置退出的过滤器
        chainDefinition.addPathDefinition("/loginOut","logout");
        //其它的所有请求设置成需要用户
        chainDefinition.addPathDefinition("/**","user");

        return chainDefinition;
    }
}

项目地址以及shiro工作流程

在这里插入图片描述

https://github.com/bigWhiteXie/Shiro-Example.git

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot-Shiro的使用 的相关文章

  • Spring boot 404错误自定义错误响应ReST

    我正在使用 Spring boot 来托管 REST API 即使浏览器正在访问 URL 以及自定义数据结构 我也希望始终发送 JSON 响应 而不是使用标准错误响应 我可以使用 ControllerAdvice 和 ExceptionHa
  • Java:BufferedInputStream 的 available() 方法存在问题

    我正在处理以下代码 用于将大文件拆分为一组较小的文件 FileInputStream input new FileInputStream this fileToSplit BufferedInputStream iBuff new Buff
  • java模拟自定义对象

    public class MainClass public void makeCall CustomObject obj new CustomObject obj testMethod 我想进行单元测试makeCall 所以我必须嘲笑Cus
  • IntelliJ Idea,如何从控制台删除java文件目录?

    当您运行文件时 它会打开控制台窗口 并且一直在顶部显示该文件所在的目录 这非常令人恼火 因为现在 为了将其他行与目录混合分开 我必须在启动任何 System out println 命令之前使用 n C Program FILEs 我想摆脱
  • 使用 Thymeleaf 时我们应该删除 HTML 属性吗?

    我正在研究 Thymeleaf 发现几乎所有示例中都有 Thymeleaf 的标签值以及标准 HTML 值 例如 这些
  • 大型 XML 的 XML 节点到字符串转换

    到目前为止我一直在使用DOM源在我的 Android 应用程序中将 XML 文件转换为字符串 这是我的代码 public String convertElementToString Node element throws Transform
  • JavaFX 动画使用循环?

    我正在尝试制作一款类似太空侵略者的游戏 我画了一个正方形 我想通过使用循环逐步向下移动它thread sleep 然而 正方形立即被绘制出来 我知道有可以使用的动画路径 但我想保持低水平并仅使用坐标系 有没有办法使用这样的循环来制作时间轴动
  • 酷还是傻? Catch(异常[NamingException, CreateException] e)

    我正在编写一些代码 我注意到异常处理中的一种模式让我思考 try do stuff throws JMS Create and NamingException catch NamingException e log1 e rollback
  • Java Timer 类:如果其中一个任务抛出异常,则计时器任务停止执行

    new Timer scheduleAtFixedRate new TimerTask Override public void run System out println run throw new SomeRandomExceptio
  • 在 Java/GWT 中解析用户时间输入

    解析用户在 GWT 中的文本字段中键入的时间的最佳方法是什么 默认时间格式要求用户完全按照区域设置指定的时间格式输入时间 我想要更加灵活 因为用户可以通过多种不同的方式输入时间 例如 8 8p 8pm 8 15pm 13 15 1315 1
  • 未从线程接收位置数据

    我尝试使用计时器经常发送包含用户位置的短信 最初 我遇到了空指针异常 这是由于我犯了一个简单的错误 一旦解决了这个问题 一切似乎都运行良好 但是 它永远不会获取我的位置 因此 不断发送的文本显示 无法接收位置 我想问的是为什么它无法获取我的
  • JavaFX 8 默认消息图标

    随着 JavaFX 的最近几次更新 我们收到了警报 我想获取消息的默认图标 错误 警告 在Swing中 我可以通过一些方式获取L F消息图标UIManager的属性 如何在 JavaFX 中获取消息的默认图标 它们是包含在属性中 还是由 C
  • 解析 SWIG 接口文件的结构属性

    这是我不久前问过的问题的延续 为通过参数返回的函数创建类型映射 https stackoverflow com questions 12793973 create a typemap for a function that returns
  • 为什么我的 Java 路径中添加了“L”?

    我在我的类路径中加载了一个 jar 在 iReport 中 如果重要的话 我确信它具有所需的方法 但是当我尝试测试连接 从而调用该 jar 时 我得到一个 java lang NoSuchMethodError 说它正在引用班上 Lorg
  • 使用 OpenNLP 获取句子的解析树。陷入困境。

    OpenNLP 是一个关于自然语言处理的 Apache 项目 NLP 程序的目标之一是解析一个句子 并给出其语法结构的树 例如 天空是蓝色的 这句话 可能会被解析为 S NP VP The sky is blue where S是句子 NP
  • 如何设置 commons-logging 来使用 logback?

    我们使用 slf4j logback 并且碰巧有一些使用 commons logging 的第三方库 如何设置它以使用 logback 答案是不要使用 commons logging jar 因为 SLF4J 的设计目的与 commons
  • 从 IntelliJ 运行 JavaFX 应用程序

    Versions openjdk版本 11 0 11 2021 04 20 OpenJDK 运行时环境 build 11 0 11 9 Ubuntu 0ubuntu2 20 10 OpenJDK 64 位服务器虚拟机 内部版本 11 0 1
  • Hibernate 命名查询使用 Like 和 % % 运算符?

    在我的 Hibernate JPA 示例代码中 public List
  • Java分数计算器

    我对 Java 编程还很陌生 我的 AP 计算机编程课程有作业要完成 所以请耐心等待 我必须弄清楚如何将两个分数相乘 我想知道是否有任何方法可以在方法内部声明变量并在该方法外部使用它 我在介绍方法中的 while 循环 谢谢您 希望这不会令
  • 是什么让热部署成为“难题”?

    在工作中 我们经常遇到这样的问题 永久代内存不足 http www jroller com agileanswers entry preventing java s java lang例外 团队负责人认为这是 JVM 中的一个错误 与代码的

随机推荐

  • 小程序`canvasToTempFilePath:fail:cearte bitmap failed?`

    这个方法的思路来源链接 微信开放社区 主要是通过延迟 重试 以及画质来解决手机性能等问题导致的canvasToImageFile故障 代码仅供参考 欢迎大家提供更多方法或思路 或指出代码异常 谢谢 下面是我用到项目中的代码片段 海报信息 P
  • PID算法的理论分析

    PID算法的理论和应用 PID算法基本原理 PID算法的离散化 PID算法伪代码 PID算法C 实现 pid cpp pid h pid example cpp Python代码 仿真结果 PID算法基本原理 PID算法是控制行业最经典 最
  • webbench剖析

    webbench 其为linux上一款web性能压力测试工具 它最多可以模拟3万个并发连接数来测试服务器压力 其原理为fork多个子进程 每个子进程都循环做web访问测试 子进程将访问的结果通过管道告诉父进程 父进程做最终结果统计 其主要原
  • javaweb 之 JDBC 详解 数据库连接池

    JDBC简介 JDBC 就是使用Java语言操作关系型数据库的一套API 全称 Java DataBase Connectivity Java 数据库连接 JDBC 本质 官方 sun公司 定义的一套操作所有关系型数据库的规则 即接口 各个
  • 基于深度学习的花卉图像关键点检测

    点击上方 小白学视觉 选择加 星标 或 置顶 重磅干货 第一时间送达 在本文中 我们描述了我们如何使用卷积神经网络 CNN 来估计花卉图像中关键点的位置 并且在 3D 模型上渲染这些图像上茎和花的位置等关键点 为了能够与真实花束的照片对比
  • C语言数码管全熄,各位大神,如何用C语言实现在数码管上实现1234同时亮

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 将移位寄存器内的数据锁存到输出寄存器并显示 void OUT 595 void RCK 595 0 nop nop
  • Linux下tar命令解压到指定的目录

    文章转自 http blog sina com cn s blog 62449fcf0100nfar html 版权归原作者 Linux下tar命令解压到指定的目录 tar zxvf bbs tar zip C zzz bbs 把根目录下的
  • POJ 2479 Dual Core CPU|网络流|dinic模版

    问题描述 总时间限制 15000ms 单个测试点时间限制 5000ms 内存限制 65536kB 描述 As more and more computers are equipped with dual core CPU SetagLilb
  • AI实战训练营&MMDetection安装配置指南

    AI实战训练营 MMDetection安装配置指南 一 MMDetection简介 版本迭代变化 2 0 3 0 二 环境检测和安装 三 准备数据集 四 自定义配置文件 一 MMDetection简介 MMDetection 是被广泛使用的
  • mq topic持久化订阅者(topic、queue的producer.setDeliveryMode(DeliveryMode. PERSISTENT)是指的mq服务),queue的消费者不在也会给

    mq topic持久化订阅者 topic queue的producer setDeliveryMode DeliveryMode PERSISTENT 是指的mq服务 queue的消费者不在也会给他保留 topic只有持久化订阅者会保留 1
  • 华为上机考试注意事项及编程技巧

    华为上机考试注意事项及编程技巧 这是一篇关于华为招聘软件类职位上机考试的博客 主要介绍一下华为机考的流程 注意事项以及一些机试题中常用的编程技巧 写得有点长 但都是尽心尽力敲的 如果真的要参加华为招聘 或者类似公司的招聘 建议稍微花点时间看
  • iOS navigationController中回到tabbarController根视图方法

    根据需求来改变跳转 self navigationController popToRootViewControllerAnimated NO self dismissViewControllerAnimated NO completion
  • Codeforces 1370 E

    题意 给定两个 01 01 01序列 S S S和 T T T 可以选择
  • 边缘提取之sobel、scharr、laplacian、canny算子

    转载自 https blog csdn net m0 37704205 article details 88699636
  • Python 生成、识别社会统一信用代码

    三证合一之后 社会统一信用代码就是企业的身份证 也就是说只要你的程序涉及企业信息的录入 那就少不了社会统一信用代码的录入 这里分享一个工具 可以用 Python 来识别社会统一信用代码 也可以随机生成社会统一信用代码 社会统一信用代码 共
  • android开发中遇到的一些问题及解决方案

    相信大家在打包也遇到过这样的问题把 打包失败 以下是昨天我昨天开发时遇到的一些问题 经过查找资料 顺利解决 不过多赘述 问题如下 问题一 Messages报错如下 Errors while building APK You can find
  • 剑指Offer62—圆圈中最后剩下的数字

    剑指Offer62 题意 0 1 n 1这n个数字排成一个圆圈 从数字0开始 每次从这个圆圈里删除第m个数字 删除后从下一个数字开始计数 求出这个圆圈里剩下的最后一个数字 例如 0 1 2 3 4这5个数字组成一个圆圈 从数字0开始每次删除
  • python——封装、继承、多态

    文章目录 继承 多继承 多态 type和isinstance的区别 类方法和静态方法 类方法 继承 class Father secrect xxx story 从前有座山 def tellAstory self print 我的故事 se
  • 低代码开发平台的优点和缺点

    随着数字化转型的加速 企业需要更快速地开发和交付应用程序 以适应市场需求和客户需求的变化 在这种情况下 低代码平台成为了企业的首选方案之一 想象一下 你可以用一个可视化工具构建自己的应用程序 而无需编写繁琐的代码 这就是低代码开发模式的魅力
  • SpringBoot-Shiro的使用

    一 什么是Shiro 权限体系在现代软件应用中有着非常重要的地位 一个应用如果没有权限体系都会显着这个系统 特别不安全 无论是传统的MIS系统还是互联网项目出于对业务数据和应用自身的安全 都会设置自己的安全策略 目前市场上专门的Java权限