通过内嵌iframe进行两系统的免密自动登录进行实现

作者: adm 分类: 前端 发布时间: 2023-10-10

光是下面的代码只能实现相同ip不同端口的跨域iframe自动登录,如果是不同域名的跨域除了下面的代码之外还需要借助nginx的代理,将不同域名的跨域转换为相同ip不同端口的跨域,nginx代理在附一中

下面的为A系统登录后跳转的首个页面,在A系统的页面中内嵌iframe,对B系统进行免密自动登录

!








B系统进行自动登录的接口

@Controller
@RequestMapping(value = "unifiedAuthentication")
public class UnifiedAuthenticationController extends BaseController {
 
 
    @RequestMapping(value = "zzz")
    public String singleLoginzz(HttpServletRequest request,HttpServletResponse response,String cid) {
                //TODO 在实际项目中需要在这个接口中添加数字签名认证,保证系统的安全性
        Subject currentUser = SecurityUtils.getSubject();
        //在这里进行shiro框架的免密自动登录
        currentUser.login(new UsernamePasswordToken(cid,"这个密码是随便填的,后续登录验证中并没有使用到".toCharArray(),true));
        //进行在第三方进行自动登录时,由于是在iframe中进行的跨域名访问,没有设置cookie的权限,因此需要添加下面两行代码
        response.setHeader("Set-Cookie", "Key=Value;SameSite=None;Secure");
        response.setHeader("Access-Control-Allow-Origin","*");
        return "redirect:"+"/";
    }
 
}

B系统的UsernamePasswordToken需要设置可以免除密码的登录

public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken {
 
 
   private static final long serialVersionUID = 1L;
 
 
   private String captcha;
   private boolean mobileLogin;
   private boolean noPassword;
 
 
   public boolean isNoPassword() {
      return noPassword;
   }
 
 
   public void setNoPassword(boolean noPassword) {
      this.noPassword = noPassword;
   }
 
 
   public UsernamePasswordToken() {
      super();
   }
   public UsernamePasswordToken(String username, char[] password,boolean noPassword) {
      super(username,password);
      this.noPassword=noPassword;
   }
 
 
   public UsernamePasswordToken(String username, char[] password,
         boolean rememberMe, String host, String captcha, boolean mobileLogin) {
      super(username, password, rememberMe, host);
      this.captcha = captcha;
      this.mobileLogin = mobileLogin;
   }
 
 
   public String getCaptcha() {
      return captcha;
   }
 
 
   public void setCaptcha(String captcha) {
      this.captcha = captcha;
   }
 
 
   public boolean isMobileLogin() {
      return mobileLogin;
   }
   
}

下面为B系统免密登录相关代码

B系统的自定义实现的验证比较器HashedCredentialsMatcherAndNoPassword

package com.jeeplus.modules.sys.security;
 
 
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SaltedAuthenticationInfo;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.crypto.hash.AbstractHash;
import org.apache.shiro.crypto.hash.Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.StringUtils;
 
 
/**
* @author: wyj
* @date: 2021/06/25
*/
public class HashedCredentialsMatcherAndNoPassword extends SimpleCredentialsMatcher {
    private String hashAlgorithm;
    private int hashIterations;
    private boolean hashSalted;
    private boolean storedCredentialsHexEncoded;
 
 
    public HashedCredentialsMatcherAndNoPassword() {
        this.hashAlgorithm = null;
        this.hashSalted = false;
        this.hashIterations = 1;
        this.storedCredentialsHexEncoded = true;
    }
 
 
    public HashedCredentialsMatcherAndNoPassword(String hashAlgorithmName) {
        this();
        if (!StringUtils.hasText(hashAlgorithmName)) {
            throw new IllegalArgumentException("hashAlgorithmName cannot be null or empty.");
        } else {
            this.hashAlgorithm = hashAlgorithmName;
        }
    }
 
 
    public String getHashAlgorithmName() {
        return this.hashAlgorithm;
    }
 
 
    public void setHashAlgorithmName(String hashAlgorithmName) {
        this.hashAlgorithm = hashAlgorithmName;
    }
 
 
    public boolean isStoredCredentialsHexEncoded() {
        return this.storedCredentialsHexEncoded;
    }
 
 
    public void setStoredCredentialsHexEncoded(boolean storedCredentialsHexEncoded) {
        this.storedCredentialsHexEncoded = storedCredentialsHexEncoded;
    }
 
 
    /** @deprecated */
    @Deprecated
    public boolean isHashSalted() {
        return this.hashSalted;
    }
 
 
    /** @deprecated */
    @Deprecated
    public void setHashSalted(boolean hashSalted) {
        this.hashSalted = hashSalted;
    }
 
 
    public int getHashIterations() {
        return this.hashIterations;
    }
 
 
    public void setHashIterations(int hashIterations) {
        if (hashIterations < 1) {
            this.hashIterations = 1;
        } else {
            this.hashIterations = hashIterations;
        }
 
 
    }
 
 
    /** @deprecated */
    @Deprecated
    protected Object getSalt(AuthenticationToken token) {
        return token.getPrincipal();
    }
 
 
    protected Object getCredentials(AuthenticationInfo info) {
        Object credentials = info.getCredentials();
        byte[] storedBytes = this.toBytes(credentials);
        if (credentials instanceof String || credentials instanceof char[]) {
            if (this.isStoredCredentialsHexEncoded()) {
                storedBytes = Hex.decode(storedBytes);
            } else {
                storedBytes = Base64.decode(storedBytes);
            }
        }
 
 
        AbstractHash hash = this.newHashInstance();
        hash.setBytes(storedBytes);
        return hash;
    }
 
//免密登录的修改
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        Object tokenHashedCredentials = this.hashProvidedCredentials(token, info);
        Object accountCredentials = this.getCredentials(info);
        UsernamePasswordToken token1 = (UsernamePasswordToken) token;
        if(token1.isNoPassword()){
            return true;
        }
        return this.equals(tokenHashedCredentials, accountCredentials);
    }
 
 
    protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
        Object salt = null;
        if (info instanceof SaltedAuthenticationInfo) {
            salt = ((SaltedAuthenticationInfo)info).getCredentialsSalt();
        } else if (this.isHashSalted()) {
            salt = this.getSalt(token);
        }
 
 
        return this.hashProvidedCredentials(token.getCredentials(), salt, this.getHashIterations());
    }
 
 
    private String assertHashAlgorithmName() throws IllegalStateException {
        String hashAlgorithmName = this.getHashAlgorithmName();
        if (hashAlgorithmName == null) {
            String msg = "Required 'hashAlgorithmName' property has not been set.  This is required to execute the hashing algorithm.";
            throw new IllegalStateException(msg);
        } else {
            return hashAlgorithmName;
        }
    }
 
 
    protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
        String hashAlgorithmName = this.assertHashAlgorithmName();
        return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
    }
 
 
    protected AbstractHash newHashInstance() {
        String hashAlgorithmName = this.assertHashAlgorithmName();
        return new SimpleHash(hashAlgorithmName);
    }
}

B系统的realm中指定使用上面的比较器

SystemAuthorizingRealm

@Service
//@DependsOn({"userMapper","roleMapper","menuMapper"})
public class SystemAuthorizingRealm extends AuthorizingRealm {
    @PostConstruct
   public void initCredentialsMatcher() {
//    HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(SystemService.HASH_ALGORITHM);
      HashedCredentialsMatcherAndNoPassword matcher = new HashedCredentialsMatcherAndNoPassword(SystemService.HASH_ALGORITHM);
      matcher.setHashIterations(SystemService.HASH_INTERATIONS);
      setCredentialsMatcher(matcher);
   }
}

附一

通过nginx将不同域名的跨域转换为相同ip不同端口的跨域

A系统地址:http://888.888.888:7070

B系统地址:http://999.999:999:8080

在nginx.conf中配置代理

    upstream bserver{            
       #B系统的ip地址:B系统的端口号                                             
        server 999.999:999:8080;                                                
        keepalive 2000;
    }
    server {
        #与A系统不同的端口号
        listen       9000;                                                         
        server_name  localhost;                                               
        client_max_body_size 1024M;
 
        location / {
            proxy_pass http://bserver/;
            proxy_set_header Host $host:$server_port;
        }
    }

在A系统中嵌入iframe的页面

!






如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!