需求:对接第三方登陆,实现绕过原有Shiro认证登陆。

文章目录

一、实现思路
1. 现状分析

系统权框架默认使用Shiro 认证授权机制

2. 用户来源

从统一认证平台登录跳转过来的用户

3. 所属范围

登录限制由统一认证平台去做,但是,跳转过来的用户仍然走您本系统的登录流程,只是走本系统的登录流程时,想跳过Shiro 对用户密码的校验,校验所属范围为Shiro 认证机制,其他功能照旧;

二、实现方案
2.1. 自定义登录认证规则
package com.gblfy.config.skipshiro;

import com.gblfy.config.skipshiro.enums.ShiroApproveLoginType;
import org.apache.shiro.authc.UsernamePasswordToken;

/**
* 自定义token 实现免密和密码登录
* <p>
* 1.账号密码登陆(password)
* 2.免密登陆(nopassword)
* </p>
*
* @author gblfy
* @date 2021-10-22
*/
public class EasyUsernameToken extends UsernamePasswordToken {
private static final long serialVersionUID = -2564928913725078138L;

private ShiroApproveLoginType type;

public EasyUsernameToken() {
super();
}

/**
* 免密登录
*/
public EasyUsernameToken(String username) {
super(username, "", false, null);
this.type = ShiroApproveLoginType.NOPASSWD;
}

/**
* 账号密码登录
*/
public EasyUsernameToken(String username, String password, boolean rememberMe) {
super(username, password, rememberMe, null);
this.type = ShiroApproveLoginType.PASSWORD;
}

public ShiroApproveLoginType getType() {
return type;
}

public void setType(ShiroApproveLoginType type) {
this.type = type;
}

}
2.2. Shiro认证枚举
package com.gblfy.config.skipshiro.enums;

/**
* Shiro认证枚举
* @author gblfy
* @date 2021-10-22
*/
public enum ShiroApproveLoginType {
/** 密码登录 */
PASSWORD("PASSWORD"),
/** 密码登录 */
NOPASSWD("NOPASSWORD");
/** 状态值 */
private String code;
private ShiroApproveLoginType(String code) {
this.code = code;
}

public String getCode() {
return code;
}
}
2.3. 密码和非密码登录
package com.gblfy.config.skipshiro;

import com.gblfy.config.skipshiro.enums.ShiroApproveLoginType;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;

/**
* 自定义登录认证方案
* <p>
* 1.免密登录,不加密
* 2.密码登录,md5加密
* </p>
*
* @author gblfy
* @date 2021-10-22
*/
public class EasyCredentialsMatch extends HashedCredentialsMatcher {

/**
* 重写方法
* 区分 密码和非密码登录
* 此次无需记录登录次数 详情看SysPasswordService
*/
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
EasyUsernameToken easyUsernameToken = (EasyUsernameToken) token;

//免密登录,不验证密码
if (ShiroApproveLoginType.NOPASSWD.equals(easyUsernameToken.getType())) {
return true;
}

//密码登录
Object tokenHashedCredentials = hashProvidedCredentials(token, info);
Object accountCredentials = getCredentials(info);
return equals(tokenHashedCredentials, accountCredentials);
}
}
2.4. 规则配置
customCredentialsMatch() {
EasyCredentialsMatch customCredentialsMatch = new EasyCredentialsMatch();
customCredentialsMatch.setHashAlgorithmName("md5");
customCredentialsMatch.setHashIterations(3);
customCredentialsMatch.setStoredCredentialsHexEncoded(true);
return customCredentialsMatch;
}
2.5. 自定义Realm

权限认证 保持默认,修改登录认证

public class UserRealm extends AuthorizingRealm {

/**
* 权限认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//权限认证 代码省略
}

/**
* 登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
EasyUsernameToken upToken = (EasyUsernameToken) token;
String username = upToken.getUsername();

SysUser user = null;
// 密码登录
if (upToken.getType().getCode().equals(LoginType.PASSWORD.getCode())) {
String password;
if (upToken.getPassword() != null) {
password = new String(upToken.getPassword());
try {
user = loginService.login(username, password);
}
catch (Exception e) {
log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
throw new AuthenticationException(e.getMessage(), e);
}
}
} else if (upToken.getType().getCode().equals(LoginType.NOPASSWD.getCode())) {
// 第三方登录 TODO

}

SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, upToken.getPassword(), getName());
return info;
}
}
2.6. 案例使用
(String username, String password, Boolean rememberMe) {
EasyUsernameToken token = new EasyUsernameToken(username, password, rememberMe);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
return success();
} catch (AuthenticationException e) {
String msg = "用户或密码错误";
if (StringUtils.isNotEmpty(e.getMessage())) {
msg = e.getMessage();
}
return error(msg);
}
}