文档章节

JavaWeb数据库开发知识总结(jdbc基础)

S
 Sunmos
发布于 2017/05/13 00:19
字数 4091
阅读 4
收藏 0
点赞 0
评论 0

JavaWeb数据库开发知识总结(jdbc基础)

1. JDBC概述

JDBC:(Java DataBase Connectivity Java数据库连接),是一种用于执行SQL语句的Java的API.可以为多种关系型数据库提供统一的访问.它是由一组使用Java语言编写的类或接口组成.
主要功能是使得Java程序能够连接数据库.

JDBC驱动:

驱动是两个设备之间的通信桥梁.
Java程序需要连接数据库必须使用对应数据库的驱动,驱动就是实现了JDBC规范的数据库操作接口的Java文件.

2. 单元测试

针对Java程序中某一个方法进行的测试.
使用时需要在测试的方法上加上@Test 注解,用来表明该方法可以进行单元测试.
运行时,选中该方法以run as JUnit Test来执行该方法.
要求:单元测试的方法必须是public修饰,无返回值,方法无参数;

3. JDBC开发步骤

  • 引入要使用的数据库的驱动的jar包;
  • 加载数据库的驱动类;
  • 获取数据库的连接对象;
  • 编写SQL语句,获取SQL执行对象,执行SQL语句获得结果集对象;
  • 执行结果的处理;
  • 释放资源对象(结果集对象,执行SQL对象,数据库连接对象).

3.1 加载数据库驱动类的方式(以MySql数据库为例)

使用DriverManager类registerDriver方法加载驱动:

DriverManager.registerDriver(实现jdbc规范的Driver对象);
// 加载mysql驱动,将mysql驱动的jar包添加到BuildPath中
// mysql的jar包:mysql-connector-java-5.0.8-bin.jar
DriverManager.registerDriver(new Driver());  // Driver()是mysqljar包中的类

使用反射方式加载驱动:

Class.forName(数据库驱动Driver类的全路径名);
// 加载mysql驱动
Class.forName("com.mysql.jdbc.Driver"); // 参数是mysql的驱动Driver的全路径名

两种加载驱动方式的比较:

使用registerDriver注册驱动有两个弊端:
 该方法是通过导入数据库驱动的类,使得程序依赖了具体的驱动类;
 mysql的Driver类源码中通过静态代码块注册了驱动类,该方式使得驱动被注册2次.
使用反射方式加载驱动:
 较为常用,避免了使用registerDriver注册驱动的弊端,官方推荐使用该方式进行驱动类的加载.

3.2 获取数据库的连接对象

Connection Conn = DriverManager.getConnection(数据库url,数据库用户名,数据库密码);
// 获取mysql的数据库连接
 DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "root", "root");

数据库url格式说明:

# 以mysql的连接为例:jdbc:mysql://localhost:3306/mydatabase
  jdbc:代表使用的是jdbc协议连接数据库;
  mysql:代表使用jdbc的子协议mysql协议(也可能为Oracle协议等);
  localhost:代表连接数据库服务器的主机名,此格式代表本地服务器,当远程连接时,将此部分换为数据库服务器的主机名或ip地址;
  3306:代表的是数据库服务器工作的端口号;
  mydatabase:代表的是使用数据库服务器中的数据库名称.
# 注意:
  当使用的本地服务器且端口号为330时,可以将localhost省略,简写为:jdbc:mysql:///mydatabase

3.3 获取执行SQL语句的对象

执行SQL语句的对象有两种:Statement和PreparedStatement.
Statement对象是直接将SQL语句进行编译执行,不能防止SQL注入问题;
PreparedStatement对象会将SQL语句进行预编译,能有效的预防SQL注入问题.

获取执行SQL语句的Statement对象:

通过数据库连接对象获取SQL执行对象
Statement stmt = 数据库连接对象.createStatement();
通过Statement对象执行SQL语句
String sql = "sql语句"; // sql语句,原始的SQL语句
ResultSet rs = stmt.executeQuery(sql语句); // 执行查询语句,返回的是查询的结果集对象
int row = stmt.executeUpdate(sql语句); // 执行增,删,改的语句,返回执行该sql语句影响的数据库中的行数

获取执行SQL语句的PreparedStatement对象:

通过数据库连接对象获取SQL执行对象,并对sql语句进行预编译
PreparedStatement ps = 数据库连接对象.PreparedStatement(sql语句);
通过PreparedStatement对象执行SQL语句
String sql = "参数化的sql语句"; // sql语句,将其中的参数使用?代替
通过PreparedStatement对象的setXXX()方法将sql语句中的参数设置值,其中的参数索引从1开始,对应字段数据类型要使用相应的设置值的方法
ps.setInt(参数索引,参数值); // 设置指定索引处的int类型的值
ps.setString(参数索引,参数值); // 设置指定索引处的String类型的值
ResultSet rs = ps.executeQuery(); // 执行查询语句,返回的是查询的结果集对象
int row = ps.executeUpdate(); // 执行增,删,改的语句,返回执行该sql语句影响的数据库中的行数

Statement和PreparedStatement对象的区别:

# Statement对象会编译每一个传递的SQL语句,效率较低;不进行预编译检查sql语句,会在数据库中执行传入的原始SQL语句,因此无法避免SQL注入的问题.
# PrepareedStatement对象对于相同的SQL语句只会编译一次,预编译后只需要对应的传入参数,效率较高;进行预编译检查SQL语句,会将其中的语句中特殊字符进行转义,有效的避免了SQL注入问题.

3.4 释放资源对象

数据库的连接数量是有限的,使用数据库连接需要遵循`晚创建,早释放`的原则.
为确保数据库连接对象能够及时关闭,通常将资源的释放代码放在finally代码块中.

资源释放标准代码:

// 释放结果集对象资源
if(rs != null) { // 当资源对象不为null时,才释放资源
    try {
        rs.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    rs = null; // 将资源对象置为空,JVM可以尽早回收
}
// 释放执行SQL对象资源
if(stmt != null) {
    try {
        stmt.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    stmt = null;
}
// 释放数据库连接资源
if(conn != null) {
    try {
        conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    conn = null;
    }
}

3.5 案例代码

public class My_JDBC_Demo01 {
    // 单元测试
    @Test
    public void demo() throws SQLException, ClassNotFoundException {
        // 加载驱动,方式1
        // DriverManager.registerDriver(new Driver());
        // 加载驱动,方式2,反射
        Class.forName("com.mysql.jdbc.Driver");
        // 获得连接
        Connection con = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/jdbc", "root", "root");
        // 数据库连接的url简写形式
        // Connection con = DriverManager.getConnection("jdbc:mysql:///jdbc","root", "root");
        // 创建SQL语句,并执行
        String sql = "select * from user";
        // 获取执行SQL语句的对象
        Statement sta = con.createStatement();
        // 执行SQL语句并获取结果集
        ResultSet rs = sta.executeQuery(sql);
        // 遍历结果集
        while (rs.next()) {
            // 获取查询结果集中的id字段的值
            int id = rs.getInt("id");
            // 获取查询结果集中的username字段的值
            String username = rs.getString("username");
            // 获取查询结果集中的password字段的值
            String password = rs.getString("password");
            // 输出结果集中每一行的值
            System.out.println(id + "---" + username + "---" + password);
        }
        // 释放资源
        rs.close();
        sta.close();
        con.close();
    }
    /* * 释放资源标准写法 */
    @Test
    public void demo2() {
        // 定义资源对象
        Connection conn = null; // 连接对象
        PreparedStatement ps = null; // 执行SQL语句对象
        ResultSet rs = null; // 结果集对象
        try {
            // 加载驱动,反射方式
            Class.forName("com.mysql.jdbc.Driver");
            // 获得连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "root");
            // 创建SQL语句,并执行
            String sql = "select * from user where id = ?";
            // 获取执行SQL语句的对象
            ps = conn.prepareStatement(sql);
            // 设置sql语句中的参数
            ps.setInt(1, 1); // sql中的第一个参数的值为1
            // 执行SQL语句并获取结果集
            rs = ps.executeQuery(sql);
            // 遍历结果集
            while (rs.next()) {
                // 获取查询结果集中的id字段的值
                int id = rs.getInt("id");
                // 获取查询结果集中的username字段的值
                String username = rs.getString("username");
                // 获取查询结果集中的password字段的值
                String password = rs.getString("password");
                // 输出结果集中每一行的值
                System.out.println(id + "---" + username + "---" + password);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放结果集对象资源
            if(rs != null) { // 当资源对象不为null时,才释放资源
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                rs = null; // 将资源对象置为空,JVM可以尽早回收
            }
            // 释放执行SQL对象资源
            if(ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                ps = null;
            }
            // 释放数据库连接资源
            if(conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn = null;
            }
        }
    }
}

4. JDBC中相关类

4.1 Connection类

Connection类是由DriverManager类获取连接的返回对象.代表一个数据库连接对象.
主要作用有两个方面:创建执行SQL语句的对象和存储事务的管理.

创建执行SQL对象:

Statement createStatement():创建Statement对象来将SQL语句发送到数据库;
PreparedStatement prepareStatement(String sql):创建PreparedStatement对象来将参数化的SQL语句发送到数据库;
CallableStatement prepareCall(String sql):创建CallableStatement对象来调用数据库存储过程.

事务管理:

void setAutoCommit(boolean autoCommit):将此连接的自动提交模式设置为给定状态,true代表自动提交事务,false代表禁止自动提交事务,默认是自动提交事务;
void commit():使所有上一次提交/回滚后进行的更改成为持久更改,并释放此Connection对象当前持有的所有数据库锁,此方法只应该在已禁用自动提交模式时使用;
void rollback():取消在当前事务中进行的所有更改,并释放此Connection对象当前持有的所有数据库锁,此方法只应该在已禁用自动提交模式时使用.

4.2 Statement和PreparedStatement对象

Statement和PreparedStatement对象由Connection对象创建,代表是可以执行SQL语句的类.
主要作用有两个方面:执行SQL语句和执行批处理.

执行SQL语句:

Statement类方法:
  ResultSet executeQuery(String sql):执行给定的SQL语句,该语句返回单个ResultSet结果集对象.
  int executeUpdate(String sql):执行给定SQL语句,该语句可能为INSERT,UPDATE,DELETE语句,对于SQL数据操作语言语句,返回行计数,对于什么都不返回的SQL语句,返回0;
  boolean execute(String sql):执行SQL语句,执行select,insert,update,delete语句,返回的是boolean值,如果返回的结果为ResultSet结果集,那么boolean值为true.如果返回的是更新的记录数.那么boolean就为false.
PreparedStatement类方法:
  ResultSet executeQuery():在此PreparedStatement对象中执行SQL查询,并返回该查询生成的ResultSet对象;
  int executeUpdate():在此PreparedStatement对象中执行SQL语句,该语句必须是一个SQL数据操作语言语句;
  boolean execute():在此PreparedStatement对象中执行SQL语句,该语句可以是任何种类的SQL语句,如果第一个结果是 ResultSet对象,则返回true,如果第一个结果是更新计数或者没有结果,则返回false.
注意事项:
execute()方法的效率较低,当能确定执行的SQL语句的类型时,尽量使用对用的SQL执行对象.

执行批处理:

Statement类的批处理方法:PreparedStatement类无批处理
void addBatch():将一组参数添加到此PreparedStatement对象的批处理命令中;
void clearBatch():清空此Statement对象的当前SQL命令列表;
int[] executeBatch():将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组.
注意事项:
执行完一次批处理命令后,需要调用清空当前SQL命令列表(clearBatch方法完成).

4.3 ResultSet类

ResultSet对象由Statement或者PreparedStatement对象中executeQuery()方法获取的返回值;
ResultSet对象代表的是select查询后的结果;
在ResultSet内部维护了一个指向标哥数据行的游标Cursor,初始化时候,游标在第一行之前的位置.调用ResultSet中next()方法.可以使游标指向具体的数据行,进而调用方法获得该行的数据.

操作结果集中数据的方法:

boolean next():将光标从当前位置向下移一行;
从结果结果集中获取当前光标指向的行的数据:
getXXX(字段名字符串或该结果集中某一列字段的值); // 列的序号从1开始
getInt(String 字段名); // 获取指定字段名的整型数据
getInt(int 字段所在的列); // 获取指定列的改行的整型值
getString(String 字段名); // 获取指定字段名的字符串数据
getString(int 字段所在的列); // 获取指定列的改行的字符串值

遍历结果集数据:

while(rs.next()){
    // 按照字段名获取结果集中的数据
    int id = rs.getInt("id");
    String username = rs.getString("username");
    String password = rs.getString("password");
    // 将结果输出
    System.out.println(id+"---"+username+"---"+password);
}

5. SQL注入漏洞

# SQL注入的漏洞在早期互联网中是普遍存在.
* 已知用户名的情况下,可以对这个用户名的用户进行攻击.
* 1.攻击方式一:
 * 在用户名地方输入:aaa' or '1=1
 * 密码随意.
* 2.攻击方式二:
 * 在用户名地方输入:aaa' -- 
 * 密码随意.
# 产生原因:因为在用户名地方输入SQL的关键字.(SQL注入)
* SQL语句:
 * select * from user where username = '' and password = '';
* 产生原因一:可以绕过密码的验证
 * select * from user where username = 'aaa' or '1=1' and password = 'xxxx';
* 产生原因二:--在SQL语句中是注释标示,将密码验证代码注释
 * select * from user where username = 'aaa' -- ' and password = 'xxxx';
# 解决SQL注入的漏洞:
* 1.使用JS在文本框进行校验.校验不可以输入-,or,'特殊的字符都不可以输入.
 * 不可行:JS的校验只是为了提升用户的体验.不能真正进行校验.(JS的代码可以被绕行.)
 * 在地址上上直接输入访问路径.loginServlet?username=aaa' or '1=1&password=xxxxxx
* 2.SQL注入漏洞真正的解决方案:PreparedStatement.对SQL进行预编译.参数的地方都可以使用?作为占位符.
 * select * from user where username = ? and password = ?;
 * 再次输入aaa' or '1=1 拿到参数之后,or一些特殊的字符不当成SQL的关键字,只是当成普通的字符串进行解析;
 * 提前进行编译.编译后的SQL的格式就已经固定.

6. JDBC工具类的抽取

将数据库驱动加载,连接获取,资源释放做成一个工具类,方便调用.
将数据库的加载驱动类和连接数据库信息写在配置文件中,通过读取配置文件获取.

配置文件的编写:

项目src资源目录下创建jdbc.properties文件(内容如下):
driverClass=com.mysql.jdbc.Driver  -- 驱动名称
url=jdbc\:mysql\://localhost\:3306/jdbc -- 数据库连接url
userName=root -- 数据库用户名
passWord=root -- 数据库密码

工具类中读取配置文件:

/** * JDBC工具类,读取配置文件 */
public class JDBCUtils {
    // 数据库驱动
    private static final String DRIVER_CLASS;
    // 数据库url
    private static final String URL;
    // 数据库用户名
    private static final String USERNAME;
    // 数据库密码
    private static final String PASSWORD;
    // 静态代码块用于初始化
    static {
        Properties prop = null;
        try {
            InputStream is = new FileInputStream("src/jdbc.properties");
            prop = new Properties();
            prop.load(is);
        } catch (Exception e) {
            e.printStackTrace();
        }
        DRIVER_CLASS = prop.getProperty("driverClass");
        URL = prop.getProperty("url");
        USERNAME = prop.getProperty("userName");
        PASSWORD = prop.getProperty("passWord");
    }
    // 私有构造方法,禁止创建该类的对象
    private JDBCUtils(){}
    /** * 加载数据库驱动程序 */
    private static void loadDriver() {
        try {
            Class.forName(DRIVER_CLASS);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    /** * 获取数据库的连接 * @return */
    public static Connection getConnection() {
        // 先加载数据库驱动程序
        loadDriver();
        Connection conn = null;
        try {
            // 获取数据库连接并返回
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
    /** * 释放数据库的连接,用于查询语句的数据库连接释放 * @param rs 查询结果集 * @param stat 执行SQL对象 * @param conn 数据库连接对象 */
    public static void release(ResultSet rs, Statement stat, Connection conn) {
        if(rs != null) {
            try {
                rs.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if(stat != null) {
            try {
                stat.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            stat = null;
        }
        if(conn != null) {
            try {
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            conn = null;
        }
    }
    /** * 释放数据库的连接,用于释放没有结果集的连接 * @param stat 执行SQL语句对象 * @param conn 数据库连接对象 */
    public static void release(Statement stat, Connection conn) {
        if(stat != null) {
            try {
                stat.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            stat = null;
        }
        if(conn != null) {
            try {
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            conn = null;
        }
    }
}

本文转载自:http://blog.csdn.net/l631106040120/article/details/71247613

共有 人打赏支持
S
粉丝 0
博文 34
码字总数 0
作品 0
成都
Java编程基础知识点和技术点归纳

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互...

Java小辰 ⋅ 05/23 ⋅ 0

Java学习---Java简单认识

前言 小编在学习Java方面的基础知识,发现里面有很多是结合之前的语言的特点发展过来的,不同的地方是,Java有它自己的发展和特点。下面小编先简单地做一下总结,结合看过的1-2章的J2SE视频,...

m18633778874 ⋅ 04/01 ⋅ 0

书单丨5本Java后端技术书指引你快速进阶

一名Java开发工程师 不仅要对Java语言及特性有深层次的理解 而且需要掌握与Java相关的 框架、生态及后端开发知识 本文涉及多种后端开发需要掌握的技能 对于帮助提高开发能力非常有帮助 NO.1...

Java高级架构 ⋅ 05/30 ⋅ 0

Java程序员必读书单,家族又添新成员

点击关注异步图书,置顶公众号 每天与你分享IT好书 技术干货 职场知识 参与文末话题讨论,每日赠送异步图书。 ——异步小编 有些革命出其不意地吸引了全世界的眼球。Twitter、Linux操作系统和...

异步社区 ⋅ 05/09 ⋅ 0

阿里、百度等多家公司Java面试记录与总结

算算自己大概面试了近十家公司,也拿到了几个Offer,现在面试告一段落,简单总结下面试经验。 我现在主要的方向是Java服务端开发,把遇到的问题和大家分享一下,也谈谈关于技术人员如何有方向...

⋅ 02/24 ⋅ 0

Java开发|Java新手应该怎么学习|2018年Java走势|

微信跳一跳你玩过吗,你知道怎么用Java开发出来吗?有时间可以研究一下!言归正传! 在2017回,java,发展程度超出我们的想象,对技术的要求也越来越高,从最初的前端、后端的布局,目前整个堆...

糖宝_d864 ⋅ 06/08 ⋅ 0

作为一名java程序员你的薪资为什么上不去?

不管是开发、测试、运维,每个技术人员心里多多少少都有一个成为技术大牛的梦,毕竟“梦想总是要有的,万一实现了呢”!正是对技术梦的追求,促使我们不断地努力和提升自己。 很多人在问我,...

java高级架构牛人 ⋅ 05/07 ⋅ 0

编写你的第一个HelloWorld

写在前面的话 因为Java基础是以后学习框架的基石,因此开个文集首先写写Java基础,本来想直奔基础知识的介绍,但是为了保证知识的完整性,因此从Java安装和运行“hello world”开始(虽然百度...

nanaFighting ⋅ 06/15 ⋅ 0

Java开发者不会这些永远都只能是三流程序员,细数一下你是不是?

源码系列 手写spring mvc框架 基于Spring JDBC手写ORM框架 实现自己的MyBatis Spring AOP实战之源码分析 Spring IOC高级特性应用分析 ORM框架底层实现原理剖析 手写Spring MVC框架实现 手把手...

美的让人心动 ⋅ 04/16 ⋅ 0

安卓开发必备知识体系:Java篇

大家好我是张拭心,自从各位朋友帮点广X开始,我发现我每天更有奔头了,走起路来也更有劲了,说啥也得更新的勤快一点。不过放心,我一定推送有价值的内容给大家,还请朋友们照旧动动手指点点...

d29h1jqy3akvx ⋅ 05/10 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

oAuth2 升级Spring Cloud Finchley.RELEASE踩坑分享

背景 6.19号,spring团队发布了期待已久的 Spring Cloud Finchley.RELEASE 版本。 重要变化: 基于Spring Boot 2.0.X 不兼容 Spring Boot 1.5.X 期间踩过几个坑,分享出来给大伙,主要是关于...

冷冷gg ⋅ 32分钟前 ⋅ 0

OSChina 周一乱弹 —— 理发师小姐姐的魔法

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @冰冰棒- :分享田馥甄的单曲《My Love》 《My Love》- 田馥甄 手机党少年们想听歌,请使劲儿戳(这里) @Li-Wang :哎,头发又长了。。。又要...

小小编辑 ⋅ 57分钟前 ⋅ 4

Kafka1.0.X_消费者API详解2

偏移量由消费者管理 kafka Consumer Api还提供了自己存储offset的功能,将offset和data做到原子性,可以让消费具有Exactly Once 的语义,比kafka默认的At-least Once更强大 消费者从指定分区...

特拉仔 ⋅ 今天 ⋅ 0

个人博客的运营模式能否学习TMALL天猫质量为上?

心情随笔|个人博客的运营模式能否学习TMALL天猫质量为上? 中国的互联网已经发展了很多年了,记得在十年前,个人博客十分流行,大量的人都在写博客,而且质量还不错,很多高质量的文章都是在...

原创小博客 ⋅ 今天 ⋅ 0

JavaScript零基础入门——(十一)JavaScript的DOM操作

JavaScript零基础入门——(十一)JavaScript的DOM操作 大家好,欢迎回到我们的JavaScript零基础入门。最近有些同学问我说,我讲的的比书上的精简不少。其实呢,我主要讲的是我在开发中经常会...

JandenMa ⋅ 今天 ⋅ 0

volatile和synchronized的区别

volatile和synchronized的区别 在讲这个之前需要先了解下JMM(Java memory Model :java内存模型):并发过程中如何处理可见性、原子性、有序性的问题--建立JMM模型 详情请看:https://baike.b...

MarinJ_Shao ⋅ 今天 ⋅ 0

深入分析Kubernetes Critical Pod(一)

Author: xidianwangtao@gmail.com 摘要:大家在部署Kubernetes集群AddOn组件的时候,经常会看到Annotation scheduler.alpha.kubernetes.io/critical-pod"="",以表示这是一个关键服务,那你知...

WaltonWang ⋅ 今天 ⋅ 0

原子性 - synchronized关键词

原子性概念 原子性提供了程序的互斥操作,同一时刻只能有一个线程能对某块代码进行操作。 原子性的实现方式 在jdk中,原子性的实现方式主要分为: synchronized:关键词,它依赖于JVM,保证了同...

dotleo ⋅ 今天 ⋅ 0

【2018.06.22学习笔记】【linux高级知识 14.4-15.3】

14.4 exportfs命令 14.5 NFS客户端问题 15.1 FTP介绍 15.2/15.3 使用vsftpd搭建ftp

lgsxp ⋅ 今天 ⋅ 0

JeeSite 4.0 功能权限管理基础(Shiro)

Shiro是Apache的一个开源框架,是一个权限管理的框架,实现用户认证、用户授权等。 只要有用户参与一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户...

ThinkGem ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部