您的位置:首页 > 其它

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

2018-01-31 21:10 441 查看
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>
还需要注意的一点是,在配置认证策略时要结合你的功能配置,我就是由于没注意到这点,吃了个大亏,集中策略如下图所示:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: