Shiro免密登录

目录

1.千篇一律

2.点睛之笔


所做项目与第三方合作,系统间存在接口调用,需要做授权登录。我们的项目整体使用SSM框架,认证登陆采用了shiro框架,密码在数据库中经过盐值(salt)+Md5加密,外部无法获知密码明文,导致无法验证通过,所以想到了免密登录的方式解决。

在网上查阅了一些贴子,套路基本一样,照搬了全部代码,发现在强转AuthenticationToken为自定义Token时报错,父类无法强转为子类。经过研究源码,只需要再多复写一个类就可以解决。具体过程如下:

1.千篇一律

1)创建枚举类LoginType

/**
 * 登录类型
 */
public enum LoginType {
    PASSWORD("password"), // 密码登录
    NOPASSWD("nopassword"); // 免密登录
    private String code;// 状态值

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

    public String getCode() {
        return code;
    }
}

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

/**
 * 自定义token 继承UsernamePasswordToken,
 *  账号密码登陆(password) 和 免密登陆(nopassword)
 */
public class CustomeToken extends UsernamePasswordToken {
    private static final long serialVersionUID = -2564928913725078138L;

    private LoginType type;


    public CustomeToken() {
        super();
    }


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

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

    /**
     * 账号密码登录
     */
    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)修改自定义的UserRealm,这个类继承了AuthorizingRealm类。将UsernamePasswordToken usertoken = (UsernamePasswordToken)token;修改为CostomToken usertoken = (CostomToken)token;

public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {

	String loginId = (String)principals.getPrimaryPrincipal();
	SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            return authorizationInfo;
	}

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {

	//增加免密登录功能,使用自定义token
	CostomToken token = (CostomToken) authcToken;

	String loginId = (String)token.getPrincipal();

        User user = userService.findUserByLoginId(loginId);

        if(user == null) {
            throw new UnknownAccountException();
        }

        if(user.getState() != 0) {
            throw new LockedAccountException(); 
        }

        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getLoginId(),
                user.getPassword(), 
                ByteSource.Util.bytes(user.getLoginId() + user.getSalt()),//salt=username+salt
                getName()  //realm name
        );
        return authenticationInfo;
	//	return null;
	}

}

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

public class CustomCredentialsMatch extends HashedCredentialsMatcher {
    private Cache<String, AtomicInteger> passwordRetryCache;

    public CustomCredentialsMatch (CacheManager cacheManager) {
        this.passwordRetryCache = cacheManager.getCache("passwordRetryCache");
    }

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //增加免密登录功能,使用自定义token
        CustomToken usertoken = (CustomToken) token;

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

        String loginID = usertoken.getUsername();

        AtomicInteger retryTimes = passwordRetryCache.get(loginID);
        if (retryTimes == null) {
            retryTimes = new AtomicInteger(0);
            passwordRetryCache.put(loginID, retryTimes);
        }
        if (retryTimes.incrementAndGet() > 5) {
            throw new ExcessiveAttemptsException();
        }
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches)
            passwordRetryCache.remove(loginID);

        return matches;
    }
}

5)LoginContoller调用

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

6)SSM框架下的配置文件spring/spring-client-shiro.xml的部分调整

<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>

到这为止,网上的贴子基本如此,有人可能成功实现免密登录了,但是我没有成功,在认证的时候报强转失败,那么我们来看接下来怎么做。

2.点睛之笔

1)分析源码

shiro集成到springMVC中的方法,大家可以去百度中查阅,其中几个关键点这里提一下。

1.1)web.xml中配置拦截器

1.2)spring/spring-client-shiro.xml中配置各种过滤器

1.3)看FormAuthenticationFilter类源码

2)覆写FormAuthenticationFilter类的父类AuthenticatingFilter中的createToken方法

  • 原写法

  • 新方法
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
    @Override
    protected AuthenticationToken createToken(String username, String password,
                                              boolean rememberMe, String host) {
        //增加免密登录功能,使用自定义token
        return new CustomToken(username, password, LoginType.PASSWORD, rememberMe, host);
    }
}

3)配置文件修改

<!--增加免密登录功能,使用自定义token-->
    <!--<bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">-->
    <bean id="formAuthenticationFilter" class="com.xxfamly.service.security.filter.CustomFormAuthenticationFilter">
        <property name="usernameParam" value="username"/>
        <property name="passwordParam" value="password"/>
        <property name="rememberMeParam" value="rememberMe"/>
    </bean>

至此,彻底完成了免密登录的改造。

  • 4
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值