通过内嵌iframe进行两系统的免密自动登录进行实现
光是下面的代码只能实现相同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的页面
!