Shiro免密登录

做的项目与第三方OA对接,需要将待办推送至OA形成xx系统待办,点击办理跳转回我们系统进行任务办理。我们项目使用SSM框架,认证使用shiro框架,外部无法获悉密码明文,导致验证不通过,直接跳转到了登录页面,所以采用免密登录的方式解决此问题。

1、创建登录类型枚举类

/**
 * @Type LoginType.java
 * @Desc
 * @author
 * @date 2020/05/29
 * @version
 */
public enum LoginType {
	PASSWORD("password"), NOPASSWORD("nopassword");
	private String code;


	private LoginType(String password) {
		this.code = code;
	}

	public String getCode(){
		return code;
	}
}

2、自定义CustomeToken,继承UsernamePasswordToken,通过构造方法区分密码登录和免密登录

/**
 * @Type CustomeToken.java
 * @Desc
 * @author 王宇飞
 * @date 2020/05/29
 * @version
 */
public class CustomeToken extends UsernamePasswordToken {

	private LoginType type;

	public CustomeToken(String userName, String password, LoginType type, boolean rememberMe, String host) {
		super(userName, password, rememberMe, host);
		this.type = type;
	}

	/**
	 * 免密登录构造方法
	 * @param userName
	 */
	public CustomeToken(String userName) {
		super(userName, "", false, null);
		this.type = LoginType.NOPASSWORD;
	}

	/**
	 * 用户名密码登录构造方法
	 * @param userName
	 * @param password
	 */
	public CustomeToken(String userName, String password) {
		super(userName, password, false, null);
		this.type = LoginType.PASSWORD;
	}

	public LoginType getType() {
		return type;
	}

	public void setType(LoginType type) {
		this.type = type;

	}
}

3、修改自定义ShiroDbRelam,这个类继承了AuthorizingRelam类。将UsernamePasswordToken token = (UsernamePasswordToken)authcToken;改为CustomeToken token = (CustomeToken) authcToken;

/**
     * Shiro登录认证(原理:用户提交 用户名和密码  --- shiro 封装令牌 ---- realm 通过用户名将密码查询返回 ---- shiro 自动去比较查询出密码和用户输入密码是否一致---- 进行登陆控制 )
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authcToken) throws AuthenticationException {
        LOGGER.info("Shiro开始登录认证");
		CustomeToken token = (CustomeToken) authcToken;
        Account accountVO = new Account();
        accountVO.setLoginName(token.getUsername());
        List<Account> list = accountService.selectByLoginName(accountVO);
        // 账号不存在
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        Account account = list.get(0);
        // 账号未启用
        if (account.getStatus() == 1) {
            return null;
        }
        // 读取用户的url和角色
        Map<String, Set<String>> resourceMap = roleService.selectResourceMapByUserId(account.getId());
        Set<String> urls = resourceMap.get("urls");
        Set<String> roles = resourceMap.get("roles");
        ShiroUser shiroUser = new ShiroUser(account.getId(), account.getLoginName(), account.getName(), urls);
        shiroUser.setRoles(roles);
        // 认证缓存信息
        return new SimpleAuthenticationInfo(shiroUser, account.getPassword().toCharArray(), 
                ShiroByteSource.of(account.getSalt()), getName());
    }

4、自定义RetryLimitCredentialsMatcher类继承HashedCredentialsMatcher类,覆写其中的doCredentialsMatch方法,将token强转为自定义token,若loginType是免密登录,则直接返回true,否则执行父类比对。

@Override
	public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
		//增加免密登录功能,使用自定义token
		CustomeToken userToken = (CustomeToken) authcToken;
		//如果登录类型为免密登录,不需要校验密码
		if(LoginType.NOPASSWORD.equals(userToken.getType())){
			return true;
		}

		String username = (String) authcToken.getPrincipal();
		//retry count + 1
		AtomicInteger retryCount = passwordRetryCache.get(username);
		if(retryCount == null) {
			retryCount = new AtomicInteger(0);
			passwordRetryCache.put(username, retryCount);
		}
		if(retryCount.incrementAndGet() > 5) {
			//if retry count > 5 throw
			logger.warn("username: " + username + " tried to login more than 5 times in period");  
			throw new ExcessiveAttemptsException("用户名: " + username + " 密码连续输入错误超过5次,锁定半小时!"); 
		}

		boolean matches = super.doCredentialsMatch(authcToken, info);
		if(matches) {
			//clear retry data
			passwordRetryCache.remove(username);
		}
		return matches;
	}

5、LoginController调用
1)密码登录

Subject subject = SecurityUtils.getSubject();
CustomToken  token = new CustomToken(username,password);
subject.login(token);

2)免密登录

Subject subject = SecurityUtils.getSubject();
//增加免密登录功能,使用自定义token
CustomToken token = new CustomToken(username);
subject.login(token);

6、调整shiro配置文件

<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
   <property name="cacheManagerConfigFile" value="classpath:ehcache/ehcache-shiro.xml"/>
</bean>
 
<bean id="credentialsMatcher"
    class="com.xxfamly.service.security.credentials.CustomCredentialsMatch">
    <constructor-arg ref="cacheManager" />
    <property name="hashAlgorithmName" value="md5" />
    <property name="hashIterations" value="3" />
    <property name="storedCredentialsHexEncoded" value="true" />
</bean>
    
<!-- Realm实现 -->
<bean id="userRealm" class="com.xxfamly.service.security.UserRealm">
     <property name="credentialsMatcher" ref="credentialsMatcher"/>
     <property name="cachingEnabled" value="false"/>
</bean>

到此为止,可以成功实现免密登录了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值