Shiro

2023-10-26

资料

概念

基本功能:

在这里插入图片描述
以下内容翻译自官网:https://shiro.apache.org/introduction.html

Shiro的目标是Shiro开发团队所说的“应用程序安全的四个基石” - 身份验证,授权,会话管理和密码学:

  • 认证:有时称为“登录”,这是证明用户是他们所说的身份的行为。

  • 授权:访问控制的过程,即确定“谁”可以访问“什么”。

  • 会话管理:管理特定于用户的会话,即使在非 Web 或 EJB 应用程序中也是如此。

  • 密码学:使用加密算法确保数据安全,同时仍然易于使用。

在不同的应用程序环境中,还有一些其他功能可以支持和加强这些问题,特别是:

  • Web 支持:Shiro 的 Web 支持 API 有助于轻松保护 Web 应用程序。

  • 缓存:缓存是Apache Shiro的API中的一级公民,以确保安全操作保持快速高效。

  • 并发:Apache Shiro支持多线程应用程序及其并发功能。

  • 测试:测试支持可帮助您编写单元测试和集成测试,并确保代码按预期得到保护。

“运行方式”:允许用户假定其他用户的身份(如果允许)的功能,有时在管理方案中很有用。

“记住我”:跨会话记住用户的身份,以便他们只需要在强制要求时登录。

架构原理

以下内容翻译自官网:https://shiro.apache.org/architecture.html

在这里插入图片描述

  • Subject:正如我们在教程中提到的,Subject本质上是当前执行用户的特定于安全的“视图”。“用户”一词通常指的是人,而主体可以是人,但它也可以代表第三方服务、守护帐户、cron作业或任何类似的东西——基本上是当前与软件交互的任何东西。
    主题实例都绑定到(并且需要)SecurityManager。当您与Subject交互时,这些交互将转换为与SecurityManager的特定于主题的交互。

  • SecurityManager: SecurityManager是Shiro架构的核心,充当一种“保护伞”对象,协调内部安全组件,共同形成对象图。然而,一旦为应用程序配置了SecurityManager及其内部对象图,它通常就不会受到影响,应用程序开发人员几乎把所有时间都花在Subject API上。
    我们将在后面详细讨论SecurityManager,但重要的是要认识到,当您与Subject交互时,实际上是幕后的SecurityManager为任何Subject安全操作做了所有繁重的工作。这反映在上面的基本流程图中。

  • Realms: Realms充当Shiro和应用程序安全数据之间的“桥梁”或“连接器”。当需要实际与安全相关数据(如用户帐户)交互以执行身份验证(登录)和授权(访问控制)时,Shiro会从为应用程序配置的一个或多个realm中查找其中的许多内容。
    从这个意义上讲,Realm本质上是一个特定于安全的DAO:它封装了数据源的连接细节,并根据需要将相关数据提供给Shiro。配置Shiro时,必须指定至少一个Realm用于身份验证和/或授权。SecurityManager可以配置多个realm,但至少需要一个。

在这里插入图片描述

  • Subject:当前与软件交互的实体(用户、第三方服务、cron作业等)的特定于安全的“视图”。

  • SecurityManager:如前所述,SecurityManager是Shiro架构的核心。它主要是一个“伞形”对象,用于协调其托管组件,以确保它们顺畅地协同工作。它还管理Shiro对每个应用程序用户的视图,因此它知道如何对每个用户执行安全操作。

  • Authenticator: Authenticator是负责执行和响应用户的身份验证(登录)尝试的组件。当用户尝试登录时,Authenticator将执行该逻辑。验证者知道如何与一个或多个存储相关用户/帐户信息的域进行协调。从这些领域获得的数据用于验证用户的身份,以确保用户确实是他们所说的那个人。

    • Authentication Strategy:如果配置了多个Realm, AuthenticationStrategy将协调各个Realm以确定身份验证尝试成功或失败的条件(例如,如果一个域成功而其他域失败,那么尝试成功吗?所有领域都必须成功吗?只有第一个?)
  • Authorizer :授权器是负责确定应用程序中用户访问控制的组件。它是一种机制,最终决定是否允许用户做某事。与验证者一样,授权者也知道如何与多个后端数据源协调以访问角色和权限信息。授权人使用此信息来确定是否允许用户执行给定的操作。

  • SessionManager: SessionManager知道如何创建和管理用户会话生命周期,为所有环境中的用户提供健壮的会话体验。这是安全框架世界中的一个独特特性——Shiro能够在任何环境中本地管理用户会话,即使没有可用的Web/Servlet或EJB容器。默认情况下,Shiro将使用现有的会话机制(例如Servlet容器),但如果没有,例如在一个独立的应用程序或非web环境中,它将使用其内置的企业会话管理来提供相同的编程体验。SessionDAO的存在是为了允许使用任何数据源持久化会话。

    • SessionDAO代表SessionManager执行会话持久性(CRUD)操作。这允许将任何数据存储插入到Session Management基础设施中。
  • CacheManager (org.apache.shiro.cache.CacheManager)创建并管理其他Shiro组件使用的Cache实例生命周期。由于Shiro可以访问许多后端数据源进行身份验证、授权和会话管理,因此缓存一直是该框架中的一流体系结构特性,可以在使用这些数据源时提高性能。任何现代开源和/或企业缓存产品都可以插入到Shiro中,以提供快速高效的用户体验。

  • Cryptography :是企业安全框架的自然补充。Shiro的加密包包含易于使用和理解的加密密码,哈希(又名摘要)和不同的编解码器实现的表示。这个包中的所有类都经过精心设计,非常易于使用和理解。任何使用过Java原生加密支持的人都知道,它是一种难以驯服的动物。Shiro的加密api简化了复杂的Java机制,使加密技术易于为普通人使用。

  • Realms:如上所述,Realms是Shiro和应用程序安全数据之间的“桥梁”或“连接器”。当需要实际与安全相关数据(如用户帐户)交互以执行身份验证(登录)和授权(访问控制)时,Shiro会从为应用程序配置的一个或多个realm中查找其中的许多内容。您可以根据需要配置任意数量的realm(通常每个数据源一个),Shiro将根据需要与它们协调进行身份验证和授权。

登录认证

概念

(1)身份验证:一般需要提供如身份ID等一些标识信息来表明登录者的身份,如提供email,用户名/密码来证明。

(2)在shiro中,用户需要提供principals(身份)和credentials (证明
给shiro,从而应用能验证用户身份

(3) principals:身份,即主体的标识属性,可以是任何属性,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary’ principals,一般是用户名/邮箱/手机号。

(4) credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。

(5)最常见的principals和crqdentials组合就是用户名/密码

流程

在这里插入图片描述
在这里插入图片描述

角色授权

概念

(1)授权,也叫访问控制,即在应用中控制谁访问哪些资源(如访问页面
编辑数据/页面澡作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)

(2)主体(Subject):访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。

(3)资源(Resource):在应用中用户可以访问的· URL,比如访问JSP页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

(4)权限(Permission):安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权权限控制)等。权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许。

(5)Shiro支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权艮,即实例级别的)

(6)角色(Role):权限的集合,一般情况下会赋予用户角色而不是权限,即这样用户可以有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程币等都是角色,不同的角色拥有一组不同的权限

流程

在这里插入图片描述

在这里插入图片描述

代码

大致流程

shiro配置解读(ShiroConfig)

1、自定义realm:

  • MyRealm是一个自定义类,继承AuthorizingRealm类
    在这里插入图片描述

2、创建安全管理器

  • 这里需要用到刚刚创建的realm
    在这里插入图片描述

3、ShiroFilter过滤器

  • 需要用到第2步的安全管理器
  • 下图中map的value可取值:
    • anon:无需认证就可以访问
    • authc:必须认证了才能让问
    • user:必须拥行记住我功能才能用
    • perms:圳有对某个资源的权限才能访问;
    • role:拥有某个角色权限才能访问
    • logout:设置登出的路径
      在这里插入图片描述

登录/认证:

我们在配置类配置了登录页的地址
在这里插入图片描述
当访问登录页,或访问某个需要认证才能访问的资源时,就会跳转到登录页,然后进入登录认证流程,大致流程为:

1、填写登录表单(login.html)
在这里插入图片描述
2、来到controller:(UserController )

  • 在这里,我们需要获取到加密后的密码,并执行subject.login方法
    在这里插入图片描述

3、来到自定义Realm:(MyRealm)

  • 自定义Realm实现AuthorizingRealm
  • 认证对应于doGetAuthenticationInfo方法,另一个是授权用的
  • 在这里,我们只需告诉shiro真实的密码应该是数目即可。
  • SimpleAuthenticationInfo的参数:
    • 第一个,类似于身份标识符,在这存什么,在其他类就能取到什么
    • 第二个,用户对应的密码
    • 第三个,空串即可
  • 如果这里传的密码,和第2步controller里面那传的密码相同就认证成功(shiro来完成)
    在这里插入图片描述

授权

1、配置了需要某个权限才能访问的资源:(两种方式)

  • 在配置类中配置:(ShiroConfig )
    在这里插入图片描述
  • 使用注解配置:(UserController)
    在这里插入图片描述

2、当访问对应资源时,将会来到自定义Realm:(MyRealm )

  • 对应的方法是:doGetAuthorizationInfo
  • 我们只用告诉shiro该用户有哪些权限即可,其他的由shiro来完成
    在这里插入图片描述

详细代码:

pom

  • (spring-boot我用的2.2.6.RELEASE)
        <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring-boot-starter -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.10.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/user/login" method="post">
    提示:用户名任意,密码是1234即可,root用户有user:*权限<br>
    username:<input name="username"><br>
    username:<input name="password"><br>
    <input type="submit">
</form>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>首页</h1>
<hr>
<a href="/user/add">添加用户</a>|
<a href="/user/delete">删除用户</a>|
<a href="/user/logout">退出登录</a>
</body>
</html>

UserController

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@Slf4j
public class UserController {
    /**
     * 去填写登录表单
     *
     * @return
     */
    @GetMapping({"/user/login", "/"})
    public String login() {
        return "redirect:/login.html";
    }

    /**
     * 去首页(需要先登录,具体的请看:ShiroConfig)
     *
     * @return
     */
    @GetMapping("/user/index")
    public String index() {
        return "redirect:/index.html";
    }

    /**
     * 添加用户(需要user:add权限),现假设有权限就返回添加成功。使用配置类ShiroConfig实现。
     * 相关代码:map.put("/user/add","perms[user:add]");
     */
    @GetMapping("/user/add")
    @ResponseBody
    public String add() {
        return "添加成功...";
    }
    /**
     * 删除用户(需要user:delete权限),现假设有权限就返回删除成功。使用注解实现。
     * 相关注解:@Requires*
     */
    @GetMapping("/user/delete")
    @ResponseBody
    @RequiresPermissions("user:delete")
    public String delete() {
        return "删除成功...";
    }

    /**
     * 登录(认证),具体逻辑在:MyRealm
     *
     * @param username
     * @param password
     * @return
     */
    @PostMapping("/user/login")
    public String login(String username, String password) {
        try {
            //获取加密后的密码
            password = new Md5Hash(password, "salt", 3).toHex();
            //获取主题对象
            Subject subject = SecurityUtils.getSubject();
            //登录(第三个参数,是否记住我)
            subject.login(new UsernamePasswordToken(username, password));
            System.out.println(username + ",登录成功!!!");
            return "redirect:/index.html";
        }
        //以下是可能出现的异常...
        catch (UnknownAccountException uae) {
            System.out.println("用户名不存在");
        } catch (IncorrectCredentialsException ice) {
            System.out.println("密码不正确");
        } catch (LockedAccountException lae) {
            System.out.println("帐户锁定异常");
        } catch (AuthenticationException ae) {
            System.out.println("其他登录异常...");
        }
        return "redirect:/login.html";
    }
}

ShiroConfig

import com.ljy.realm.MyRealm;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
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;

@Configuration
public class ShiroConfig {
    /**
     * ShiroFilter过滤所有请求
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //给ShiroFilter配置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //配置系统公共资源、受限资源
        Map<String, String> map = new HashMap<>();
        /**
         * anon:无需认证就可以访问
         * authc:必须认证了才能让问
         * user:必须拥行记住我功能才能用
         * perms:圳有对某个资源的权限才能访问;
         * role:拥有某个角色权限才能访问
         * logout:设置登出的路径
         */
        //无需认证
        map.put("/user/login","anon");
        //需要认证(登录)
        map.put("/user/index","authc");
        //需要有某个权限
        map.put("/user/add","perms[user:add]");
        //设置登出的路径
        map.put("/user/logout","logout");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        // 设置认证(登录)界面路径
        shiroFilterFactoryBean.setLoginUrl("/user/login");

        return shiroFilterFactoryBean;
    }

    /**
     * 创建安全管理器
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(Realm realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm,也可以通过setRealms设置多个
        securityManager.setRealm(realm);

        //设置认证策略示例:
        ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
        //有三种策略:
        //  AtLeastOneSuccessfulStrategy(至少有一个realm成功)
        //  FirstSuccessfulStrategy(有一个成功即可,后面的将被忽略)
        //  AllSuccessfulStrategy(所有的都要成功)
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());

        return securityManager;
    }

    /**
     * 创建自定义Realm
     */
    @Bean
    public Realm realm() {
        return new MyRealm();
    }
}

MyRealm

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.ArrayList;
import java.util.List;

/**
 * 自定义Realm
 */
public class MyRealm extends AuthorizingRealm {
    /**
     * 授权
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //获取用户名
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        //根据用户名查权限
        List<String> perms = getPermsByUsername(username);
        if (perms != null && !perms.isEmpty()){
            info.addStringPermissions(perms);
        }

        return info;
    }

    /**
     * 认证
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();

        //根据用户名获取对应的密码
        String password = getPasswordByUsername(username);

        //剩下的事,交给shiro
        //第一个参数,是一个对象(例如,可以使用Admin、User等),在其它类中可以使用SecurityUtils.getSubject().getPrincipal()获取。
        // 这里为了简单,将用户名作为主题
        return new SimpleAuthenticationInfo(username, password, "");
    }

    /**
     * 根据用户名查密码。一般通过Service、Dao查询数据库进行实现。
     * 这里为了简单,将密码同意设置为1234(md5加密)
     *
     * @param username
     * @return
     */
    public String getPasswordByUsername(String username) {
        //加盐,盐值是“salt”,迭代三次
        return new Md5Hash("1234", "salt", 3).toHex();
    }

    /**
     * 根据用户名查询用户的权限。一般通过Service、Dao查询数据库进行实现。
     * 这里为了简单,就给root用户user:*权限
     * @param principal
     * @return
     */
    public List<String> getPermsByUsername(String principal) {
        if (principal.equals("root")){
            ArrayList<String> list = new ArrayList<>();
            list.add("user:*");
            return list;
        }
        return null;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Shiro 的相关文章

随机推荐

  • vscode好用的前端插件和快捷键

    用到好用的vscode插件 总结一下 文章目录 一 常用主题 1 Material Theme主题 2 Community Material Theme主题 3 vscode icons 二 基础插件 1 Code Spell checke
  • java生成二维码图片(有logo),并在图片下方附文字

    logo配置类 Created by Amber Wang on 2017 11 27 17 25 import java awt public class LogoConfig logo默认边框颜色 public static final
  • 【数据结构】 实现 堆 结构 ---超细致解析

    目录 二叉树的性质 二叉树的存储结构 顺序存储 链式存储 堆的概念和性质 堆的实现 堆的初始化 堆的插入 向上调整函数 堆的删除 向下调整函数 向上建堆 向下建堆 TopK问题 二叉树的性质 在我们实现堆之前我们要知道堆的实现是依靠的是二叉
  • C# Thread启动线程时传递参数

    目录 1 不带参数 使用ThreadStart 2 带一个参数 使用ParameterizedThreadStart 3 带多个参数 1 不带参数 使用ThreadStart private void funcName public voi
  • Java反射学习记录

    一 反射概述 反射允许程序在运行中获取类的内部信息 例如构造器 成员变量 成员方法等 类加载之后 在堆中生成一个Class类的对象 一个类只有一个Class对象 这个对象包含类的完整结构信息 二 入门案例 通过配置文件中的内容生成指定类的对
  • 创建完整团队的艺术:敏捷如何改变我们与客户的工作方式

    来源 Ackarlix博客 http www ackarlix com 十年前 敏捷宣言 的作者们希望我们重新思考 我们作为程序员与客户协作的方式 我和我的博士学位顾问Robert Biddle以及James Noble都深受启发 充满希望
  • python --- multiprocessing实现多进程

    文章目录 进程理论知识 multiprocessing模块实现多进程 进程池 进程理论知识 进程就是正在运行的程序 是计算机进行资源分配的最小单位 各个进程都有独立的数据 相互隔离 Linux里进程的状态 R 运行状态runable S 中
  • 各种网络协议的类型、优缺点、作用

    一 网络协议的定义 网络协议是一种特殊的软件 是计算机网络实现其功能的基本机制 网络协议的本质是规则 即各种硬件和软件必须遵循的共同规则 网络协议并不是一套单独的软件 他融合于其他所有软件系统中 协议在网络中无所 不在 二 常用的网络协议
  • 24

    以下内容出自 MySQL 实战 45 讲 https time geekbang org column article 76446 24 MySQL是怎么保证主备一致的 MySQL 主备的基本原理 如图所示就是基本的主备切换流程 M S结构
  • socket可读,可写的条件

    socket可读可写条件 经常做为面试题被问 因为它考察被面试者对网络编程的基础了解的是不是够深入 要了解socket可读可写条件 我们先了解几个概念 1 接收缓存区低水位标记 用于读 和发送缓存区低水位标记 用于写 每个套接字有一个接收低
  • Oracle数据库运维、备份常用指令

    Oracle数据库运维 备份常用指令 1 Oracle数据泵备份导出 1 1 准备工作 在linux系统下创建导出结果存放的文件夹 切记要切换到oracle用户创建 否则会出现权限问题 su oracle mkdir home oracle
  • keepalived高可用服务的VIP地址无法访问

    环境 keepalived nginx实现高可用 VIP地址可以正常生成 也可正常漂移 可以实现故障切换 VIP地址只能在本地服务器ping通 其他内网服务器上无法ping通VIP地址 防火墙和selinux都已关闭 原因一 服务器启动了i
  • day37 445 数字反转 (字符串处理、模拟)

    445 数字反转 给定一个整数 请将该数各个位上数字反转得到一个新数 新数也应满足整数的常见形式 即除非给定的原数为零 否则反转后得到的新数的最高位数字不应为零 输入格式 输入共1行 1个整数N 输出格式 输出共1行 1个整数表示反转后的新
  • 集群基础7——keepalived脑裂

    文章目录 一 脑裂概念 二 脑裂产生原因 三 解决方案 四 脑裂监控 一 脑裂概念 在高可用 HA 系统中 当联系2个节点的 心跳线 断开时 本来为一整体 动作协调的HA系统 就分裂成为2个独立的个体 由于相互失去了联系 都以为是对方出了故
  • 常用 Git 命令行操作

    本文记录了一些常用 Git 命令行操作的具体使用方式 git clone git clone REPOSITORY URL 拉取仓库 并使用仓库名作为本地文件名 git clone REPOSITORY URL FOLDER 拉取仓库 并使
  • android new intent(),Android:关于onNewIntent()触发机制及注意事项

    在阅读该篇日志前 先熟悉一下Android的四种启动模式 因为onNewIntent并不是在所有启动模式下都会执行的 一 onNewIntent 在IntentActivity中重写下列方法 onCreate onStart onResta
  • Flink水位线不触发问题

    窗口计算时遇到好几次水位线不触发的情况 简单总结下 首先 介绍下Flink的事件时间 EventTime 和水位线 Watermarks 的概念 一 处理时间 如果要构造一个实时的流式应用 或早或晚都会接触到EventTime这个概念 现实
  • 用vscode写markdown的正确姿势

    写markdown一直没找到特别顺手的工具 这两天试了试vscode 被惊艳到到了 特此记录一下 1 安装vscode 之所以罗嗦一下安装 是因为安装版明显不如解压绿色版好用 特此标记一下 2 安装markdown插件 vscode默认是支
  • 机器学习——模型评估

    在学习得到的模型投放使用之前 通常需要对其进行性能评估 为此 需使用一个 测试集 testing set 来测试模型对新样本的泛化能力 然后以测试集上的 测试误差 tootino error 作为泛化误差的近似 我们假设测试集是从样本真实分
  • Shiro

    文章目录 资料 概念 基本功能 架构原理 登录认证 概念 流程 角色授权 概念 流程 代码 大致流程 shiro配置解读 ShiroConfig 登录 认证 授权 详细代码 pom login html index html UserCon