SpringBoot integra Shiro para lograr funciones de inicio de sesión con un solo clic y sin contraseña

premisa:

Probablemente sea tal situación. Tenemos dos plataformas, una es la plataforma de operación y la otra es la plataforma de uso. Cada uno de nuestro personal de operación y mantenimiento administra muchas cuentas de clientes en sus manos, con el fin de facilitar la operación y el mantenimiento y evitar con frecuencia pidiendo contraseñas a los usuarios. Entonces, en este caso, debemos usar nuestra función de inicio de sesión con un solo clic,

Primero, agregamos un botón de inicio de sesión con un solo clic en el sistema de administración de cuentas del cliente y luego hacemos clic en el enlace para iniciar sesión directamente en la página de inicio de la plataforma.

El proceso general es:

1: Primero agregue el código en el centro de operaciones, cifre el token designado a través de RSA, y luego lleve el token y el nombre de usuario de inicio de sesión designado después del cifrado, y luego solicite utilizar la interfaz abierta de la plataforma.

2: Use la plataforma para agregar la interfaz de descifrado e inicie sesión directamente con el usuario sin secreto después de que el descifrado sea exitoso.

No hay mucho que decir sobre el código:

1: Primero, necesitamos reescribir el doCredentialsMatch que usa el verificador de contraseñas de la plataforma. Si iniciamos sesión sin contraseña, notificaremos a Shiro que la contraseña no es necesaria para verificar la contraseña y que la autenticación es exitosa, de lo contrario, la contraseña debe ser verificado.

package com.zjxf.shiro;

import com.zjxf.bean.common.SysConst;
import com.zjxf.shiro.token.MyUserNamePasswordToken;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.springframework.context.annotation.Configuration;

/**
 * created with IntelliJ IDEA
 *
 * @author: create by limu
 * Date: 2021/1/6
 * Time:16:41
 */
@Configuration
public class MyRetryLimitCredentialsMatcher extends HashedCredentialsMatcher {

    @Override
    public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
        MyUserNamePasswordToken tk = (MyUserNamePasswordToken) authcToken;
        if (tk.getLoginType().equals(SysConst.LoginType.ONE_CLICK.getType())) {
            return Boolean.TRUE;
        }
        return super.doCredentialsMatch(authcToken, info);
    }
}

Luego configure en ShiroConfig

/**
     * 方法名:
     * 功能:凭证匹配器
     * 描述:  指定shiro加密方式和次数
     */
    @Bean
    public MyRetryLimitCredentialsMatcher hashedCredentialsMatcher() {
        MyRetryLimitCredentialsMatcher hashedCredentialsMatcher = new MyRetryLimitCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(SysConst.SHIRO_PASSWORD_TYPE_MD5);
        hashedCredentialsMatcher.setHashIterations(SysConst.SHIRO_PASSWORD_COUNT);
        return hashedCredentialsMatcher;
    }

Luego comience a escribir la interfaz descifrada:

/**
     * 远程一键登录平台
     *
     * @param authorization 登录凭证参数
     * @param timestamp     时间标识
     * @param userName      用户名称
     * @return Result
     */
    @GetMapping("oneClickLogin")
    public String oneClickLogin(@RequestParam("authToken") String authorization, @RequestParam("timestamp") String timestamp, @RequestParam("userName") String userName, ServletResponse response) throws Exception {
        if (StringUtils.isBlank(authorization) || StringUtils.isBlank(timestamp)) {
            log.info("非法请求,禁止访问");
            RequestResponseUtil.responseWrite(JSON.toJSONString("非法请求,禁止访问"), response);
        } else {
            if (DateUtils.isTimeOut(timestamp, 30)) {
                RequestResponseUtil.responseWrite(JSON.toJSONString("请求已过时"), response);
            } else {
                RemoteProperties.RSA rsa = remoteProperties.getRsa();
                String privateKey = rsa.getPrivateKey();
                String suffix = rsa.getSuffix();
                String prefix = rsa.getPrefix();

                byte[] bytes = RSAUtils.decryptByPrivateKey(Base64.decode(authorization), Base64.decode(privateKey));
                String authToken = new String(bytes);
                if (authToken.startsWith(prefix) && authToken.endsWith(suffix)) {
                    String dateStr = authToken.replace(suffix, StringUtils.EMPTY).replace(prefix, StringUtils.EMPTY);
                    if (Objects.equals(dateStr, timestamp)) {
                        log.info("请求认证成功,已经放行");
                        Subject subject = SecurityUtils.getSubject();
                        if (subject.isAuthenticated()) {
                            subject.logout();
                        }
                        MyUserNamePasswordToken token = new MyUserNamePasswordToken(userName, SysConst.LoginType.ONE_CLICK.getType());
                        subject.login(token);
                        return "html/psychological";
                    }
                } else {
                    log.info("非法请求,禁止访问");
                    RequestResponseUtil.responseWrite(JSON.toJSONString("非法请求,禁止访问"), response);
                }

            }
        }
        return "login";
    }

De esta manera, se escribe la interfaz para nuestro inicio de sesión con un clic. Por motivos de seguridad, establecemos una configuración que se detendrá directamente después de más de 30 segundos, y luego desciframos los parámetros correspondientes para iniciar sesión.

El contenido del archivo RemoteProperties es el siguiente
package com.zjxf.config.properties;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * created with IntelliJ IDEA
 *
 * @author: create by limu
 * Date: 2019/11/21
 * Time:11:50
 */
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "remote.auth")
public class RemoteProperties {

    private String host;

    private RSA rsa = new RSA();

    @Getter
    @Setter
    public static class RSA {
        private String privateKey;
        private String publicKey;
        private String prefix;
        private String suffix;
    }
}

El contenido de RSAUtils es el siguiente:

package com.zjxf.utils;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 *
 * @author zhanghao
 * date: 2018/12/13 16:37
 * description: 非对称加密算法RSA算法组件
 * 非对称算法一般是用来传送对称加密算法的密钥来使用的,相对于DH算法,RSA算法只需要一方构造密钥,不需要
 * 大费周章的构造各自本地的密钥对了。DH算法只能算法非对称算法的底层实现。而RSA算法算法实现起来较为简单
 */
public class RSAUtils {
    //非对称密钥算法
    private static final String KEY_ALGORITHM = "RSA";
    /**
     * 密钥长度,DH算法的默认密钥长度是1024
     * 密钥长度必须是64的倍数,在512到65536位之间
     */
    private static final int KEY_SIZE = 512;
    //公钥
    private static final String PUBLIC_KEY = "RSAPublicKey";

    //私钥
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /**
     * 初始化密钥对
     *
     * @return Map 甲方密钥的Map
     */
    public static Map<String, Object> initKey() throws Exception {
        //实例化密钥生成器
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        //初始化密钥生成器
        keyPairGenerator.initialize(KEY_SIZE);
        //生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        //甲方公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        //甲方私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        //将密钥存储在map中
        Map<String, Object> keyMap = new HashMap<String, Object>();
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;

    }


    /**
     * 私钥加密
     *
     * @param data 待加密数据
     * @param key  密钥
     * @return byte[] 加密数据
     */
    public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception {

        //取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私钥
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        //数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 公钥加密
     *
     * @param data 待加密数据
     * @param key  密钥
     * @return byte[] 加密数据
     */
    public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {

        //实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        //密钥材料转换
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
        //产生公钥
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);

        //数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        return cipher.doFinal(data);
    }

    /**
     * 私钥解密
     *
     * @param data 待解密数据
     * @param key  密钥
     * @return byte[] 解密数据
     */
    public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
        //取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私钥
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        //数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 公钥解密
     *
     * @param data 待解密数据
     * @param key  密钥
     * @return byte[] 解密数据
     */
    public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception {

        //实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        //密钥材料转换
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
        //产生公钥
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
        //数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, pubKey);
        return cipher.doFinal(data);
    }

    /**
     * 取得私钥
     *
     * @param keyMap 密钥map
     * @return byte[] 私钥
     */
    public static byte[] getPrivateKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return key.getEncoded();
    }

    /**
     * 取得公钥
     *
     * @param keyMap 密钥map
     * @return byte[] 公钥
     */
    public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return key.getEncoded();
    }

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        //初始化密钥
//        //生成密钥对
        Map<String, Object> keyMap = RSAUtils.initKey();
//        //公钥
        byte[] publicKey = RSAUtils.getPublicKey(keyMap);
//
//        //私钥
        byte[] privateKey = RSAUtils.getPrivateKey(keyMap);
        String publicStr = Base64.encodeBase64String(publicKey);
        String privateStr = Base64.encodeBase64String(privateKey);

//        String publicStr = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMFQhv/9PUaaG/7WfkW3P/6jTa2ed1dTNrr09pw3Jt+VU/etcKwobgpu+QD8brDzFp3TaIhPee+W7b39kCRLzlkCAwEAAQ==";
//        String privateStr = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAwVCG//09Rpob/tZ+Rbc//qNNrZ53V1M2uvT2nDcm35VT961wrChuCm75APxusPMWndNoiE9575btvf2QJEvOWQIDAQABAkBpuSK76cGTVUEuVBxnAFttZd5br6jRB1+NS997e+Y0rD8tGEEVPkIzX024OikCIjpoRZwZJFZFnTYTg2UM8b+hAiEA/B4OTyvQG3BwNy515diUGUNibmLAtx4g1/Rye1bHbN0CIQDESqXfQGHBMrCup8WJV9OX9JNQwDP8Cz9Y9s07Di3hrQIga02Lf4zBLPyE9idzDFlKZxoz6ZFkPku3ZNJoazA6/o0CIA6ydCb6IBeiHv6Ey1KUQ+CNzHXwXjQR94VGvWkdj6vlAiEAsfZ0Z8JMEp0ywT4FG/z9q1VBHdPAwPSYEVQbmQkGhaw=";


        System.out.println("公钥:\n" + publicStr);
        System.out.println("私钥:\n" + privateStr);


        System.out.println("================密钥对构造完毕 开始进行加密数据的传输=============");
        System.out.println("\n===========公钥加密==============");
        long timeMillis = System.currentTimeMillis();
        System.out.println("timeMillis = " + timeMillis);
        String str = "zkdj" + timeMillis + "yuqingguanjia@!123";
        System.out.println("原文:" + str);
        //公钥加密
        byte[] code1 = RSAUtils.encryptByPublicKey(str.getBytes(), Base64.decodeBase64(publicStr));
        System.out.println("加密后的数据:" + Base64.encodeBase64String(code1));
        System.out.println("===========私钥解密==============");
        //乙方进行数据的解密
        byte[] decode1 = RSAUtils.decryptByPrivateKey(code1, Base64.decodeBase64(privateStr));
        System.out.println("解密后的数据:" + new String(decode1) + "\n\n");

//        System.out.println("===========反向进行操作,乙方向甲方发送数据==============\n\n");
//
//        str = "乙方向甲方发送数据RSA算法";
//
//        System.out.println("原文:" + str);
//
//        //乙方使用公钥对数据进行加密
//        byte[] code2 = RSAUtils.encryptByPublicKey(str.getBytes(), publicKey);
//        System.out.println("===========乙方使用公钥对数据进行加密==============");
//        System.out.println("加密后的数据:" + Base64.encodeBase64String(code2));
//
//        System.out.println("=============乙方将数据传送给甲方======================");
//        System.out.println("===========甲方使用私钥对数据进行解密==============");
//
//        //甲方使用私钥对数据进行解密
//        byte[] decode2 = RSAUtils.decryptByPrivateKey(code2, privateKey);
//
//        System.out.println("甲方解密后的数据:" + new String(decode2));
    }
}

Configuración de Application.yml de remote.auth:

#一键登录公钥私钥
remote:
  auth:
    host: http://127.0.0.1:8631/login
    rsa:
      public-key: 
      private-key:
      prefix: zjxf
      suffix: 

En cuanto a la clave pública y la clave privada, puede utilizar RSA para generar un conjunto de claves públicas y privadas. Al cifrar, utilice la clave pública para cifrar, al descifrar, utilice la clave privada para descifrar, al operar la plataforma, utilice el clave pública para cifrar y utilizar la plataforma para descifrar. Utilice la clave privada para descifrar,

Puede personalizar los campos de prefijo y sufijo, o no necesita configurarlos. Este es un método de encriptación.

Luego, puede acceder directamente a la dirección de inicio de sesión con un solo clic:

http://127.0.0.1:8631/apis/remote/oneClickLogin?authToken=SUEgd2h7ApystCI2kEu8aYVPSvcs/JaDcfpmRn5z7EFANq4mOqsbmy0d3GEdSawV8HjXjDIE2Nombre de usuario y tiempo de administración de 50%

De esta forma, puede iniciar sesión directamente en el contenido de la página de inicio de la plataforma.

Supongo que te gusta

Origin blog.csdn.net/qq_38821574/article/details/112285080
Recomendado
Clasificación