需求:对接第三方登陆,实现绕过原有Shiro认证登陆。
文章目录
- 一、实现思路
- 1. 现状分析
- 2. 用户来源
- 3. 所属范围
- 二、实现方案
- 2.1. 自定义登录认证规则
- 2.2. Shiro认证枚举
- 2.3. 密码和非密码登录
- 2.4. 规则配置
- 2.5. 自定义Realm
- 2.6. 案例使用
一、实现思路
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*/@Overridepublic 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. 规则配置
@Beanpublic EasyCredentialsMatch customCredentialsMatch() {EasyCredentialsMatch customCredentialsMatch = new EasyCredentialsMatch();customCredentialsMatch.setHashAlgorithmName("md5");customCredentialsMatch.setHashIterations(3);customCredentialsMatch.setStoredCredentialsHexEncoded(true);return customCredentialsMatch;}
2.5. 自定义Realm
权限认证 保持默认,修改登录认证
public class UserRealm extends AuthorizingRealm {/*** 权限认证 */@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//权限认证 代码省略}/*** 登录认证*/@Overrideprotected 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. 案例使用
public AjaxResult login(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);}}