`
hetylei
  • 浏览: 140417 次
  • 性别: Icon_minigender_1
  • 来自: 青岛
社区版块
存档分类
最新评论

xmpp with openfire之四 扩展的AuthProvider

阅读更多
上一篇中提到jdbcAuthProvider.passwordType提供了三种方式

如果你的密码加密规则不是这三种方式,可以自己进行扩充

首先,下载openfire的源码
http://www.igniterealtime.org/downloads/source.jsp

打开org.jivesoftware.openfire.auth.JDBCAuthProvider
package org.jivesoftware.openfire.auth;

import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;

import java.sql.*;

/**
 * The JDBC auth provider allows you to authenticate users against any database
 * that you can connect to with JDBC. It can be used along with the
 * {@link HybridAuthProvider hybrid} auth provider, so that you can also have
 * XMPP-only users that won't pollute your external data.<p>
 *
 * To enable this provider, set the following in the system properties:
 * <ul>
 * <li><tt>provider.auth.className = org.jivesoftware.openfire.auth.JDBCAuthProvider</tt></li>
 * </ul>
 *
 * You'll also need to set your JDBC driver, connection string, and SQL statements:
 *
 * <ul>
 * <li><tt>jdbcProvider.driver = com.mysql.jdbc.Driver</tt></li>
 * <li><tt>jdbcProvider.connectionString = jdbc:mysql://localhost/dbname?user=username&amp;password=secret</tt></li>
 * <li><tt>jdbcAuthProvider.passwordSQL = SELECT password FROM user_account WHERE username=?</tt></li>
 * <li><tt>jdbcAuthProvider.passwordType = plain</tt></li>
 * <li><tt>jdbcAuthProvider.allowUpdate = true</tt></li>
 * <li><tt>jdbcAuthProvider.setPasswordSQL = UPDATE user_account SET password=? WHERE username=?</tt></li>
 * </ul>
 *
 * The passwordType setting tells Openfire how the password is stored. Setting the value
 * is optional (when not set, it defaults to "plain"). The valid values are:<ul>
 *      <li>{@link PasswordType#plain plain}
 *      <li>{@link PasswordType#md5 md5}
 *      <li>{@link PasswordType#sha1 sha1}
 *  </ul>
 *
 * @author David Snopek
 */
public class JDBCAuthProvider implements AuthProvider {

    private String connectionString;

    private String passwordSQL;
    private String setPasswordSQL;
    private PasswordType passwordType;
    private boolean allowUpdate;

    /**
     * Constructs a new JDBC authentication provider.
     */
    public JDBCAuthProvider() {
        // Convert XML based provider setup to Database based
        JiveGlobals.migrateProperty("jdbcProvider.driver");
        JiveGlobals.migrateProperty("jdbcProvider.connectionString");
        JiveGlobals.migrateProperty("jdbcAuthProvider.passwordSQL");
        JiveGlobals.migrateProperty("jdbcAuthProvider.passwordType");
        JiveGlobals.migrateProperty("jdbcAuthProvider.setPasswordSQL");
        JiveGlobals.migrateProperty("jdbcAuthProvider.allowUpdate");

        // Load the JDBC driver and connection string.
        String jdbcDriver = JiveGlobals.getProperty("jdbcProvider.driver");
        try {
            Class.forName(jdbcDriver).newInstance();
        }
        catch (Exception e) {
            Log.error("Unable to load JDBC driver: " + jdbcDriver, e);
            return;
        }
        connectionString = JiveGlobals.getProperty("jdbcProvider.connectionString");

        // Load SQL statements.
        passwordSQL = JiveGlobals.getProperty("jdbcAuthProvider.passwordSQL");
        setPasswordSQL = JiveGlobals.getProperty("jdbcAuthProvider.setPasswordSQL");

        allowUpdate = JiveGlobals.getBooleanProperty("jdbcAuthProvider.allowUpdate",false);

        passwordType = PasswordType.plain;
        try {
            passwordType = PasswordType.valueOf(
                    JiveGlobals.getProperty("jdbcAuthProvider.passwordType", "plain"));
        }
        catch (IllegalArgumentException iae) {
            Log.error(iae);
        }
    }

    public void authenticate(String username, String password) throws UnauthorizedException {
        if (username == null || password == null) {
            throw new UnauthorizedException();
        }
        username = username.trim().toLowerCase();
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain. Return authentication failed.
                throw new UnauthorizedException();
            }
        }
        String userPassword;
        try {
            userPassword = getPasswordValue(username);
        }
        catch (UserNotFoundException unfe) {
            throw new UnauthorizedException();
        }
        // If the user's password doesn't match the password passed in, authentication
        // should fail.
        if (passwordType == PasswordType.md5) {
            password = StringUtils.hash(password, "MD5");
        }
        else if (passwordType == PasswordType.sha1) {
            password = StringUtils.hash(password, "SHA-1");
        }
        if (!password.equals(userPassword)) {
            throw new UnauthorizedException();
        }

        // Got this far, so the user must be authorized.
        createUser(username);
    }

    public void authenticate(String username, String token, String digest)
            throws UnauthorizedException
    {
        if (passwordType != PasswordType.plain) {
            throw new UnsupportedOperationException("Digest authentication not supported for "
                    + "password type " + passwordType);
        }
        if (username == null || token == null || digest == null) {
            throw new UnauthorizedException();
        }
        username = username.trim().toLowerCase();
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain. Return authentication failed.
                throw new UnauthorizedException();
            }
        }
        String password;
        try {
            password = getPasswordValue(username);
        }
        catch (UserNotFoundException unfe) {
            throw new UnauthorizedException();
        }
        String anticipatedDigest = AuthFactory.createDigest(token, password);
        if (!digest.equalsIgnoreCase(anticipatedDigest)) {
            throw new UnauthorizedException();
        }

        // Got this far, so the user must be authorized.
        createUser(username);
    }

    public boolean isPlainSupported() {
        // If the auth SQL is defined, plain text authentication is supported.
        return (passwordSQL != null);
    }

    public boolean isDigestSupported() {
        // The auth SQL must be defined and the password type is supported.
        return (passwordSQL != null && passwordType == PasswordType.plain);
    }

    public String getPassword(String username) throws UserNotFoundException,
            UnsupportedOperationException
    {

        if (!supportsPasswordRetrieval()) {
            throw new UnsupportedOperationException();
        }
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain.
                throw new UserNotFoundException();
            }
        }
        return getPasswordValue(username);
    }

    public void setPassword(String username, String password)
            throws UserNotFoundException, UnsupportedOperationException
    {
        if (allowUpdate && setPasswordSQL != null) {
            setPasswordValue(username, password);
        } else { 
            throw new UnsupportedOperationException();
        }
    }

    public boolean supportsPasswordRetrieval() {
        return (passwordSQL != null && passwordType == PasswordType.plain);
    }

    /**
     * Returns the value of the password field. It will be in plain text or hashed
     * format, depending on the password type.
     *
     * @param username user to retrieve the password field for
     * @return the password value.
     * @throws UserNotFoundException if the given user could not be loaded.
     */
    private String getPasswordValue(String username) throws UserNotFoundException {
        String password = null;
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain.
                throw new UserNotFoundException();
            }
        }
        try {
            con = DriverManager.getConnection(connectionString);
            pstmt = con.prepareStatement(passwordSQL);
            pstmt.setString(1, username);

            rs = pstmt.executeQuery();

            // If the query had no results, the username and password
            // did not match a user record. Therefore, throw an exception.
            if (!rs.next()) {
                throw new UserNotFoundException();
            }
            password = rs.getString(1);
        }
        catch (SQLException e) {
            Log.error("Exception in JDBCAuthProvider", e);
            throw new UserNotFoundException();
        }
        finally {
            DbConnectionManager.closeConnection(rs, pstmt, con);
        }
        return password;
    }

    private void setPasswordValue(String username, String password) throws UserNotFoundException {
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain.
                throw new UserNotFoundException();
            }
        }
        try {
            con = DriverManager.getConnection(connectionString);
            pstmt = con.prepareStatement(setPasswordSQL);
            pstmt.setString(1, username);
            if (passwordType == PasswordType.md5) {
                password = StringUtils.hash(password, "MD5");
            }
            else if (passwordType == PasswordType.sha1) {
                password = StringUtils.hash(password, "SHA-1");
            }
            pstmt.setString(2, password);

            rs = pstmt.executeQuery();

        }
        catch (SQLException e) {
            Log.error("Exception in JDBCAuthProvider", e);
            throw new UserNotFoundException();
        }
        finally {
            DbConnectionManager.closeConnection(rs, pstmt, con);
        }
        
    }

    /**
     * Indicates how the password is stored.
     */
    @SuppressWarnings({"UnnecessarySemicolon"})  // Support for QDox Parser
    public enum PasswordType {

        /**
         * The password is stored as plain text.
         */
        plain,

        /**
         * The password is stored as a hex-encoded MD5 hash.
         */
        md5,

        /**
         * The password is stored as a hex-encoded SHA-1 hash.
         */
        sha1;
    }

    /**
     * Checks to see if the user exists; if not, a new user is created.
     *
     * @param username the username.
     */
    private static void createUser(String username) {
        // See if the user exists in the database. If not, automatically create them.
        UserManager userManager = UserManager.getInstance();
        try {
            userManager.getUser(username);
        }
        catch (UserNotFoundException unfe) {
            try {
                Log.debug("JDBCAuthProvider: Automatically creating new user account for " + username);
                UserManager.getUserProvider().createUser(username, StringUtils.randomString(8),
                        null, null);
            }
            catch (UserAlreadyExistsException uaee) {
                // Ignore.
            }
        }
    }
}



看以看到通过读取PASSWORDTYPE配置
JiveGlobals.migrateProperty("jdbcAuthProvider.passwordType");
。。。。。
。。。。。
。。。。。
passwordType = PasswordType.plain;  
        try {  
            passwordType = PasswordType.valueOf(  
                    JiveGlobals.getProperty("jdbcAuthProvider.passwordType", "plain"));  
        }  
        catch (IllegalArgumentException iae) {  
            Log.error(iae);  
        }  

进行密码的验证
 if (passwordType == PasswordType.md5) {
            password = StringUtils.hash(password, "MD5");
        }
        else if (passwordType == PasswordType.sha1) {
            password = StringUtils.hash(password, "SHA-1");
        }
        if (!password.equals(userPassword)) {
            throw new UnauthorizedException();
        }



这样完全可以仿照JDBCAuthProvider重新构造

package org.yxsoft.openfire.plugin;


import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.openfire.auth.*;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.database.DbConnectionManager;

import java.sql.*;
import java.security.MessageDigest;

/**
 * Created by cl
 * Date: 2008-9-4
 * Time: 9:18:26
 * 仿照JDBCAuthProvider
 * 在数据库连接上 支持user/password
 * 密码验证 使用bfmp的机制
 */
public class BfmpAuthProvider implements AuthProvider {
    private String connectionString;
    private String user;
    private String password;

    private String passwordSQL;
    private String setPasswordSQL;
    private PasswordType passwordType;
    private boolean allowUpdate;

    /**
     * 初始化
     * 比JDBCAuthProvider多支持
     * JiveGlobals.migrateProperty("jdbcProvider.url");
        JiveGlobals.migrateProperty("jdbcProvider.user");
        JiveGlobals.migrateProperty("jdbcProvider.password");
     */
    public BfmpAuthProvider() {
        // Convert XML based provider setup to Database based
        JiveGlobals.migrateProperty("jdbcProvider.driver");
        JiveGlobals.migrateProperty("jdbcProvider.url");
        JiveGlobals.migrateProperty("jdbcProvider.user");
        JiveGlobals.migrateProperty("jdbcProvider.password");

        JiveGlobals.migrateProperty("jdbcAuthProvider.passwordSQL");
        JiveGlobals.migrateProperty("jdbcAuthProvider.passwordType");
        JiveGlobals.migrateProperty("jdbcAuthProvider.setPasswordSQL");
        JiveGlobals.migrateProperty("jdbcAuthProvider.allowUpdate");
        JiveGlobals.migrateProperty("jdbcAuthProvider.passwordType");

        // Load the JDBC driver and connection string.
        String jdbcDriver = JiveGlobals.getProperty("jdbcProvider.driver");
        try {
            Class.forName(jdbcDriver).newInstance();
        }
        catch (Exception e) {
            Log.error("Unable to load JDBC driver: " + jdbcDriver, e);
            return;
        }
        connectionString = JiveGlobals.getProperty("jdbcProvider.url");
        user = JiveGlobals.getProperty("jdbcProvider.user");
        password = JiveGlobals.getProperty("jdbcProvider.password");

        // Load SQL statements.
        passwordSQL = JiveGlobals.getProperty("jdbcAuthProvider.passwordSQL");
        setPasswordSQL = JiveGlobals.getProperty("jdbcAuthProvider.setPasswordSQL");

        allowUpdate = JiveGlobals.getBooleanProperty("jdbcAuthProvider.allowUpdate",false);

        passwordType = PasswordType.plain;
        try {
            passwordType = PasswordType.valueOf(
                    JiveGlobals.getProperty("jdbcAuthProvider.passwordType", "plain"));
            Log.error("PasswordType:"+ passwordType);
        }
        catch (IllegalArgumentException iae) {
            Log.error(iae);
        }
    }

    public boolean isPlainSupported() {
        //default
        return true;
    }

    public boolean isDigestSupported() {
        //default
        return true;
    }

    public void authenticate(String username, String password) throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException {
        if (username == null || password == null) {
            throw new UnauthorizedException();
        }
        Log.error(username+":"+password);
        username = username.trim().toLowerCase();
        if (username.contains("@")) {
            Log.error(username+":"+XMPPServer.getInstance().getServerInfo().getXMPPDomain());
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain. Return authentication failed.
                throw new UnauthorizedException();
            }
        } else {
            Log.error("user name not contains ");
        }
        String userPassword;
        try {
            userPassword = getPasswordValue(username);
        }
        catch (UserNotFoundException unfe) {
            throw new UnauthorizedException();
        }
        // If the user's password doesn't match the password passed in, authentication
        // should fail.
        if (passwordType == PasswordType.bfmp) {
            //这里BfmpMD5 就是自己的密码规则
            password = BfmpMD5(password);
        }

        if (!password.equals(userPassword)) {
            throw new UnauthorizedException();
        }

        // Got this far, so the user must be authorized.
        //createUser(username);
    }

    public void authenticate(String username, String token, String digest) throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException {
        if (passwordType != PasswordType.plain) {
            throw new UnsupportedOperationException("Digest authentication not supported for "
                    + "password type " + passwordType);
        }
        if (username == null || token == null || digest == null) {
            throw new UnauthorizedException();
        }
        username = username.trim().toLowerCase();
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain. Return authentication failed.
                throw new UnauthorizedException();
            }
        }
        String password;
        try {
            password = getPasswordValue(username);
        }
        catch (UserNotFoundException unfe) {
            throw new UnauthorizedException();
        }
        String anticipatedDigest = AuthFactory.createDigest(token, password);
        if (!digest.equalsIgnoreCase(anticipatedDigest)) {
            throw new UnauthorizedException();
        }

        // Got this far, so the user must be authorized.
        //createUser(username);
    }

    public String getPassword(String username) throws UserNotFoundException, UnsupportedOperationException {
        if (!supportsPasswordRetrieval()) {
            throw new UnsupportedOperationException();
        }
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain.
                throw new UserNotFoundException();
            }
        }
        return getPasswordValue(username);
    }

    private String getPasswordValue(String username) throws UserNotFoundException {
        String password = null;
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain.
                throw new UserNotFoundException();
            }
        }
        try {
            con = DriverManager.getConnection(connectionString, user, this.password);
            pstmt = con.prepareStatement(passwordSQL);
            pstmt.setString(1, username);

            rs = pstmt.executeQuery();

            // If the query had no results, the username and password
            // did not match a user record. Therefore, throw an exception.
            if (!rs.next()) {
                throw new UserNotFoundException();
            }
            password = rs.getString(1);
        }
        catch (SQLException e) {
            Log.error("Exception in JDBCAuthProvider", e);
            throw new UserNotFoundException();
        }
        finally {
            DbConnectionManager.closeConnection(rs, pstmt, con);
        }
        return password;
    }

    public void setPassword(String username, String password) throws UserNotFoundException, UnsupportedOperationException {
        // unsupport
    }

    public boolean supportsPasswordRetrieval() {
        return true;
    }


    /**
     * Indicates how the password is stored.
     */
    @SuppressWarnings({"UnnecessarySemicolon"})  // Support for QDox Parser
    public enum PasswordType {

        /**
         * The password is stored as plain text.
         */
        plain,

        /**
         * The password is stored as a bfmp passwrod.
         */
        bfmp;
    }

    private String BfmpMD5 (String source) {
        //在这里实现你的加密机制,返回生成的密码
        return "";

    }


}




编译后将此class加入到lib/openfire.jar中即可

当然不要忘记 将系统属性中
provider.auth.className,改成你自已的Provider
*上述代码例子中是org.yxsoft.openfire.plugin.BFMPAuthProvider
jdbcAuthProvider.passwordType,改成你自己定义的枚举值
*上述代码例子中是bfmp

分享到:
评论
1 楼 moistrot 2010-04-30  
将这个编译好的类,加入我的lib\openfire.jar,之后,登录不进去,无论我输入什么,都是密码错误。

相关推荐

    Xmpp和OpenFire实例

    先说一下为什么要写这篇博客,是因为本人在周末在研究XMPP和OpenFire,从网上下载了个Demo,但跑不起来,花了很长时间,经改造后,跑起来了,写个篇博文也是希望后边学习XMPP和OpenFire的同学下载后直接运行,少走...

    XMPP开发Openfire详细介绍

    Openfire的强大之处在于其高度可扩展的插件系统。通过安装插件,可以极大地增强服务器的功能。 ##### 3.1 KrakenIMGateway插件 - **功能**:支持MSNS、QQ等第三方即时通讯工具的登录。 - **配置**:通过插件配置...

    多人在线聊天系统源码 xmpp+openfire

    XMPP的核心设计原则是分散式和可扩展性,使得开发者可以轻松地添加新功能。在多人聊天系统中,XMPP负责处理用户的登录、注销、消息传递、群组管理等核心功能。用户通过连接到XMPP服务器,发送和接收消息,建立和管理...

    xmpp,openfire搭建ppt

    **XMPP与Openfire搭建详解** XMPP(Extensible Messaging and Presence Protocol)是一种基于XML的实时通讯协议,常用于构建即时通讯系统。它允许用户进行一对一、一对多的消息传输,同时还支持状态呈现、群组聊天...

    MongoDB分片集群搭建教程:副本集创建与数据分片

    内容概要:本文提供了详细的MongoDB分片集群的搭建指导,涵盖了从环境准备、配置文件编写、副本集的建立、主节点的选择、配置服务器和数据分片服务器的配置到最后的路由节点的搭建与操作整个流程,以及对数据库的哈希与范围两种分片策略的应用介绍和具体命令执行。 适合人群:熟悉NoSQL数据库概念并对MongoDB有一定了解的技术人员,尤其是在大型数据管理和分布式数据库架构设计中有需求的开发者。 使用场景及目标:帮助技术人员掌握构建高效能、高可用性的MongoDB分片集群的方法,适用于处理大规模、实时性强的数据存储与读取场景。 其他说明:文中通过实例演示了每个步骤的具体操作方法,便于跟随文档实操,同时也介绍了可能遇到的问题及其解决方案,如在没有正确配置的情况下试图写入数据时出现错误等情况的处理。

    CPPC++_嵌入式硬件的物联网解决方案blinker库与Arduino ESP8266 ESP32一起工作.zip

    CPPC++_嵌入式硬件的物联网解决方案blinker库与Arduino ESP8266 ESP32一起工作

    CPPC++_逆向调用QQ Mojo IPC与WeChat XPlugin.zip

    CPPC++_逆向调用QQ Mojo IPC与WeChat XPlugin

    CPPC++_现代活动指标.zip

    CPPC++_现代活动指标

    CPPC++_Xournal是一款手写笔记软件,支持PDF注释,使用C语言编写,支持GTK3,支持Linux,如Ubu.zip

    CPPC++_Xournal是一款手写笔记软件,支持PDF注释,使用C语言编写,支持GTK3,支持Linux,如Ubu

    基于SSM学生实习管理系统前台小程序与后台管理系统开发实践

    资源概述: 本资源提供了一套完整的学生实习管理系统解决方案,涵盖了前台小程序页面与后台管理系统两大模块。前台小程序页面设计简洁直观,用户可根据不同身份(学生或企业)进行登录。学生用户能够方便地浏览并投递感兴趣的实习岗位,而企业用户则能轻松发布实习信息,吸引优秀人才。后台管理系统功能全面,包括个人中心、首页、学生管理、教师管理、企业管理、招聘管理、评分管理以及实习管理等多个方面,为管理员提供了强大的数据管理和操作工具。 技术栈亮点: SSM框架:系统后台采用Spring、Spring MVC和MyBatis Plus(简称SSM)作为核心开发框架,确保了系统的稳定性、可扩展性和可维护性。Spring作为控制反转(IoC)和面向切面编程(AOP)的容器,为系统提供了强大的业务逻辑处理能力;Spring MVC则负责处理Web请求和响应,实现了前后端的分离;MyBatis Plus作为持久层框架,简化了数据库操作,提高了开发效率。 MySQL数据库:系统采用MySQL作为数据库存储解决方案,支持大数据量的存储和高效查询。 如有侵权请联系我删除,谢谢

    微服务闪聚支付项目.zip

    微服务闪聚支付项目

    Rust 与 Java 互调实战示例

    博客链接 https://blog.csdn.net/weixin_47560078/article/details/143714557 文章从原理介绍出发,实现了 Rust 与 Java 的互调。利用 JNI 技术,可以充分发挥 Rust 的性能优势,同时保持 Java 的跨平台特性。这种技术组合适用于对性能要求较高的应用场景,如图像处理、数据分析和系统级编程等。

    CPPC++_这是我翻译的艾根中文文档.zip

    cppc++

    Matlab实现斑马优化算法ZOA-TCN-Multihead-Attention多输入单输出回归预测算法研究.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    Matlab实现雪融优化算法SAO-TCN-Multihead-Attention多输入单输出回归预测算法研究.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    分布式事务lcn.zip

    分布式事务lcn

    基于Simulink的正弦波PWM技术和三次谐波注入PWM技术研究.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    【风电功率预测】基于BiTCN的风电功率多变量输入预测研究附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    CPPC++_这是由一块迷你带OV2640双DRV8833驱动TypeC接口PSRAM的ESP32PicoD4开发板驱.zip

    cppc++

    JAVA安卓手机与电脑的socket通信源码数据库 其他源码类型 WinForm

    安卓手机与电脑的socket通信源码

Global site tag (gtag.js) - Google Analytics