shiro实现手机验证码登录(涉及到:自定义token、多realm配置、自定义ModularRealmAuthenticator)

5 篇文章 0 订阅
3 篇文章 0 订阅

shiro框架提供了一个UsernamePasswordToken令牌,用来验证用户名和密码类的登录。那如果想要通过替他方式登录认证,例如通过手机验证码接口,就需要通过自定义token、自定义realm等来实现。

1、首先,自定义一个token继承UsernamePasswordToken,为什么要继承这个类而不是AuthenticationToken?,是因为这样做保证了用户名密码认证方式任然能正常使用。代码如下。

package com.java.travel.token;

import java.io.Serializable;

import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;

public class UserNamePasswordTelphoneToken extends UsernamePasswordToken implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 4812793519945855483L;

	// 手机号码
	private String telphoneNum;

	/**
	 * 重写getPrincipal方法
	 */
	public Object getPrincipal() {
		// TODO Auto-generated method stub
		// 如果获取到用户名,则返回用户名,否则返回电话号码
		if (telphoneNum == null) {
			return getUsername();
		} else {
			return getTelphoneNum();
		}
	}

	/**
	 * 重写getCredentials方法
	 */
	public Object getCredentials() {
		// TODO Auto-generated method stub
		// 如果获取到密码,则返回密码,否则返回null
		if (telphoneNum == null) {
			return getPassword();
		} else {
			return "ok";
		}
	}

	public UserNamePasswordTelphoneToken() {
		// TODO Auto-generated constructor stub
	}

	public UserNamePasswordTelphoneToken(final String telphoneNum) {
		// TODO Auto-generated constructor stub
		this.telphoneNum = telphoneNum;
	}

	public UserNamePasswordTelphoneToken(final String userName, final String password) {
		// TODO Auto-generated constructor stub
		super(userName, password);
	}

	public String getTelphoneNum() {
		return telphoneNum;
	}

	public void setTelphoneNum(String telphoneNum) {
		this.telphoneNum = telphoneNum;
	}

	public static long getSerialversionuid() {
		return serialVersionUID;
	}

	@Override
	public String toString() {
		return "TelphoneToken [telphoneNum=" + telphoneNum + "]";
	}

}
重写了getPrincipal方法和getCredentials方法,getPrincipal方法如果是用用户名和密码方式登录的则就返回用户名,手机验证码登录则表示手机号码,同理getCredentials在用户名密码登录中表示密码,在验证码登录中则什么都不表示,返回一个任意的字符串就可以了,不能返回null,否则认证不会通过的。

2、自定义一个ModularRealmAuthenticator的子类,重写doAuthenticate方法,这个方法的功能是用来决定单realm或者多realm时应该怎么做的,代码如下:

package com.java.travel.ModularRealmAuthenticator;

import java.util.Collection;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;

import com.java.travel.token.UserNamePasswordTelphoneToken;

public class MyModularRealmAuthenticator extends ModularRealmAuthenticator {
	@Override
	protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
			throws AuthenticationException {
		// TODO Auto-generated method stub
		
		// 判断getRealms()是否返回为空
        assertRealmsConfigured();
        // 强制转换回自定义的CustomizedToken
        UserNamePasswordTelphoneToken telphoneToken = (UserNamePasswordTelphoneToken) authenticationToken;
        // 所有Realm
        Collection<Realm> realms = getRealms();
     // 判断是单Realm还是多Realm
        if (realms.size() == 1)
            return doSingleRealmAuthentication(realms.iterator().next(), telphoneToken);
        else
            return doMultiRealmAuthentication(realms, telphoneToken);
	}
}
3、自定义realm,继承于AuthorizingRealm类,重写doGetAuthorizationInfo和doGetAuthenticationInfo这两个方法,前者是用来做授权处理的,后者用来身份认证。代码如下:

package com.java.travel.realm;

import javax.annotation.Resource;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import com.java.travel.entity.ExUser;
import com.java.travel.service.ExUserService;

public class TelphoneRealm extends AuthorizingRealm{

	@Resource
	ExUserService exUserService;
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// TODO Auto-generated method stub
		String telphoneNum = (String) token.getPrincipal();
		ExUser exUser = exUserService.selectByTelphoneNum(telphoneNum);
		if (exUser != null) {
			AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(exUser.getTEL(), "ok", "xx");
			return authcInfo;
		} else {		
			return null;
		}
	}

}
这里要注意AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(exUser.getTEL(), "ok", "xx");这个语句中SimpleAuthenticationInfo的第二个参数设为和自定义token中返回的一样。

4、控制器代码如下:

/**
	 * 短信验证码登录
	 * @param telphoneNum
	 * @return
	 */
	@RequestMapping(value = "codeLogin", method = RequestMethod.GET)
	@ResponseBody
	public int codeLogin(String telphoneNum) {		
		Subject subject = SecurityUtils.getSubject();
		UserNamePasswordTelphoneToken token = new UserNamePasswordTelphoneToken(telphoneNum);
		try {
			subject.login(token);
			return 1;
		}  catch (Exception e) {
			return -1;
		}
	}

5、多realm的配置,shiro的其他配置就不贴了,网上很多的。在securityManager配置属性authenticator为自定义的MyModularRealmAuthenicator类,配置如下:

	<!-- shiro的配置 -->
	<!-- 自定义Realm -->
	<bean id="userNamePasswordRealm" class="com.java.travel.realm.UserNamePasswordRealm" />
	<bean id="telphoneRealm" class="com.java.travel.realm.TelphoneRealm" />
	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!-- 单realm的配置 -->
		<!-- <property name="realm" ref="myRealm" /> -->
		<!-- 多realm的配置 -->
		<property name="authenticator" ref="myModularRealmAuthenticator"></property>
		<property name="realms">
			<list>
				<ref bean="userNamePasswordRealm" />
				<ref bean="telphoneRealm" />
			</list>
		</property>		 
	</bean>
	<!-- 配置多个realm的时候如何认证 -->
	<bean id="myModularRealmAuthenticator" class="com.java.travel.ModularRealmAuthenticator.MyModularRealmAuthenticator">
		<property name="authenticationStrategy">
			<!-- 认证策略 -->
			<!-- <bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean> -->
			<bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
		</property>
	</bean>
还需要注意的一点是,在配置认证策略时要结合你的功能配置,我就是由于没注意到这点,吃了个大亏,集中策略如下图所示:




评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值