实现思路

由RSA随机生成一对公钥和私钥,公钥方到客户端,私钥放到服务端,发送数据的时候由公钥对传输数据进行加密,然后发送给服务端,服务端用私钥才能对数据进行解密.下面是代码实现的例子

前端

jsencrypt.js 保存到项目中,并引入,提交明文密码之前,调用此方法加密

// 加密
function getSignString(data) {
    var encrypt = new JSEncrypt();
    // 公匙,由后端返回到前台
    var pubKey = parent.publicKey;
    encrypt.setPublicKey(pubKey);
    var encodePassword = encrypt.encrypt(data);
    return encodePassword;
}

后台传输公钥(此处前后端不分离,采用thymeleaf渲染)

<script th:inline="javascript">
        parent.publicKey = [[${#servletContext.getAttribute('publicKey')}]];
    </script>

后端

从配置文件读取预先准备好的公钥,存入servlet上下文中

package com.test.framework.startup;

import com.test.common.constant.Constants;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ServletContextAware;

import javax.servlet.ServletContext;

/** 系统启动往 servlet 上下文塞入数据
 * ClassName AppStartup
 * Description
 *
 * @author wang
 * Date 2021/3/22 11:28
 */
@Order(1)
@Component
public class AppStartup implements ApplicationRunner, ServletContextAware {

    private static final Logger log = LoggerFactory.getLogger(AppStartup.class);

    @Value("${test.kkFileUrl}")
    private String kkFileUrl;

    @Value("${test.ip}")
    private String ip;

    @Value("${server.port}")
    private String port;

    @Value("${rsa.public.key}")
    private String publicKey;

    private ServletContext servletContext;

    @Override
    public void run(ApplicationArguments args) {
        log.info("initialization ...");
        servletContext.setAttribute(Constants.KK_FILE_URL, kkFileUrl);
        servletContext.setAttribute(Constants.SERVER_URL, ip + ":" + port);
        servletContext.setAttribute(Constants.PUBLIC_KEY, publicKey);
        log.info("OK, completed");
    }


    @Override
    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }
}

工具类

package com.test.common.utils;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * 实现的思路:
 * 由RSA随机生成一对公钥和私钥,公钥方到客户端,私钥放到服务端,
 * 发送数据的时候由公钥对传输数据进行加密,然后发送给服务端,服务端用私钥才能对数据进行解密.下面是代码实现的例子
 *
 * ClassName RSAUtils
 * Description
 *
 * @author wang
 * Date 2021/7/7 10:41
 */
public class RSAUtils {


    private static Logger logger = LoggerFactory.getLogger(RSAUtils.class);


    /**
     * 用于封装随机产生的公钥与私钥
     * @param
     * @return
     */
    private static Map<Integer, String> keyMap = new HashMap<>();


    /**
     * 测试方法
     *
     * @param args
     * @return void
     * <p>
     * <p>
     * 前端用crypto-js进行加密,
     * npm i jsencrypt,
     * 然后页面头引入import JSEncrypt from 'jsencrypt';
     * const encrypt = new JSEncrypt();
     * encrypt.setPublicKey('你的公钥');
     * password = encrypt.encrypt(‘你的密码’);// 加密后的字符串
     */
    public static void main(String[] args) {
        //生成公钥和私钥
        genKeyPair();
        //加密字符串
        String message = "df723820";
        System.out.println("随机生成的公钥为:" + keyMap.get(0));
        System.out.println("随机生成的私钥为:" + keyMap.get(1));
        String messageEn = encrypt(message, keyMap.get(0));
        System.out.println("加密后的字符串为:" + messageEn);
        String messageDe = decrypt(messageEn, keyMap.get(1));
        System.out.println("还原后的字符串为:" + messageDe);
    }

    /**
     * 随机生成密钥对
     *
     * @param
     * @return void
     */
    public static void genKeyPair() {
        // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
        KeyPairGenerator keyPairGen = null;
        try {
            keyPairGen = KeyPairGenerator.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            logger.info(e.getMessage());
        }
        // 初始化密钥对生成器,密钥大小为96-1024位
        assert keyPairGen != null;
        keyPairGen.initialize(1024, new SecureRandom());
        // 生成一个密钥对,保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥
        String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
        // 得到私钥字符串
        String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
        // 将公钥和私钥保存到Map
        keyMap.put(0, publicKeyString);  //0表示公钥
        keyMap.put(1, privateKeyString);  //1表示私钥
    }

    /**
     * RSA公钥加密
     *
     * @param str       加密字符串
     * @param publicKey 公钥
     * @return 密文
     */
    public static String encrypt(String str, String publicKey) {
        //base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = null;
        String outStr = null;
        try {
            pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
        } catch (InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException | NoSuchPaddingException | NoSuchAlgorithmException e) {
            e.printStackTrace();
            logger.info(e.getMessage());
        }
        //RSA加密
        return outStr;
    }

    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 铭文
     */
    public static String decrypt(String str, String privateKey) {
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = null;
        //RSA解密
        Cipher cipher = null;
        String outStr = null;
        try {
            priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            outStr = new String(cipher.doFinal(inputByte));
        } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
            e.printStackTrace();
            logger.info(e.getMessage());
        }
        return outStr;
    }


}

登陆控制器

package com.test.project.security.system.user.controller;

import com.alibaba.fastjson.JSONObject;
import com.test.common.utils.RSAUtils;
import com.test.common.utils.ServletUtils;
import com.test.common.utils.StringUtils;
import com.test.common.utils.security.ShiroUtils;
import com.test.framework.web.controller.BaseController;
import com.test.framework.web.domain.AjaxResult;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Value;
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;

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

/**
 * 登录验证
 *
 * @author test
 */
@Controller
public class LoginController extends BaseController
{

    @Value("${rsa.private.key}")
    private String privateKey;

    @PostMapping("/login")
    @ResponseBody
    public AjaxResult ajaxLogin(HttpSession session, String username, String password, Boolean rememberMe)
    {
    	// 使用秘钥将前端加密密码解密
        password = RSAUtils.decrypt(password, privateKey);
        UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
        Subject subject = SecurityUtils.getSubject();
        try
        {
            subject.login(token);
            session.setAttribute("user", JSONObject.toJSONString(ShiroUtils.getSysUser()));
            return AjaxResult.success("操作成功", token);
        }
        catch (AuthenticationException e)
        {
            String msg = "用户或密码错误";
            if (StringUtils.isNotEmpty(e.getMessage()))
            {
                msg = e.getMessage();
            }
            return error(msg);
        }
    }


}

参考链接:添加链接描述  
参考链接:添加链接描述