SpringBootはShiroを統合して、ワンクリックログインとパスワードなしのログイン機能を実現します

前提:

おそらくそのような状況です。1つは運用プラットフォーム、もう1つは使用プラットフォームの2つのプラットフォームがあります。運用および保守担当者はそれぞれ、運用および保守を容易にし、頻繁に回避するために、多くの顧客アカウントを管理します。ユーザーにパスワードを要求します。この場合、ワンクリックログイン機能を使用する必要があります。

まず、顧客アカウント管理システムにワンクリックのログインボタンを追加し、次にリンクをクリックしてプラットフォームのホームページに直接ログインします。

一般的なプロセスは次のとおりです。

1:最初にオペレーションセンターにコードを追加し、RSAを介して指定されたトークンを暗号化し、暗号化後にトークンと指定されたログインユーザー名を運び、プラットフォームのオープンインターフェイスの使用を要求します。

2:プラットフォームを使用して復号化インターフェースを追加し、復号化が成功した後、秘密なしでユーザーに直接ログインします。

コードについて言うことはあまりありません:

1:まず、プラットフォームのパスワードベリファイアを使用するdoCredentialsMatchを書き換える必要があります。パスワードなしでログインした場合、パスワードを確認するためにパスワードは不要であり、認証が成功したことをShiroに通知します。それ以外の場合、パスワードは次のようにする必要があります。検証されます。

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);
    }
}

次に、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;
    }

次に、復号化されたインターフェイスの作成を開始します。

/**
     * 远程一键登录平台
     *
     * @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";
    }

このようにして、ワンクリックログインのインターフェースが作成されます。安全のため、30秒以上経過するとすぐにタイムアウトするように設定し、対応するパラメーターを復号化してログインします。

RemotePropertiesファイルの内容は次のとおりです
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;
    }
}

RSAUtilsの内容は次のとおりです。

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));
    }
}

remote.authのApplication.yml構成:

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

公開鍵と秘密鍵については、RSAを使用して公開鍵と秘密鍵のセットを生成できます。暗号化する場合は公開鍵を使用して暗号化し、復号化する場合は秘密鍵を使用して復号化し、プラットフォームを操作する場合は公開鍵を使用して暗号化し、プラットフォームを使用して復号化します。秘密鍵を使用して復号化します。

プレフィックスフィールドとサフィックスフィールドはカスタマイズできますが、設定する必要はありません。これは暗号化方式です。

次に、ワンクリックのログインアドレスに直接アクセスできます。

http://127.0.0.1:8631 / apis / remote / oneClickLogin?authToken = SUEgd2h7ApystCI2kEu8aYVPSvcs / JaDcfpmRn5z7EFANq4mOqsbmy0d3GEdSawV8HjXjDIEORAQ5ihUdb2EmQ ==&time

このようにして、プラットフォームのホームページのコンテンツに直接ログインできます。

おすすめ

転載: blog.csdn.net/qq_38821574/article/details/112285080