现有app上因功能扩展,需另外一部分用户登录,和原来的用户不在同一张表中。原来的shiro配置和单个realm不能满足多个表中用户(当然也可以在同一个realm中在两个表中查找,一个表查不到就去另一个表查,这种方式太笨了),所以自己尝试了以下扩展。实现不同的realm获取不同表中的用户。
一、先介绍一个用户时是怎么配置的
1、shiro.xml
2、
package com.su;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import net.zkbc.jcaptcha.util.JCaptchaUtils;
import net.zkbc.shiro.authc.IncorrectCaptchaException;
import net.zkbc.shiro.authc.UsernameCaptchaToken;
import net.zkbc.shiro.authc.UsernamePasswordCaptchaToken;
import net.zkbc.shiro.entity.ShiroUser;
import net.zkbc.shiro.service.ShiroCaptchaService;
import net.zkbc.shiro.service.ShiroUserService;
import redis.clients.jedis.Jedis;
public class ShiroCaptchaDbRealm extends ShiroDbRealm {
@Autowired(required = false)
private ShiroCaptchaService captchaService;
@Autowired
@Qualifier
private ShiroUserService shiroUserService;
@SuppressWarnings("resource")
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
String loginName = ((UsernamePasswordToken) authcToken).getUsername();
if (authcToken instanceof UsernameCaptchaToken) {
Jedis jedis = new Jedis();
String captcha = jedis.get(loginName); //TODO 获取验证码相关
byte[] salt_byte = null;
return new SimpleAuthenticationInfo(loginName, captcha, ByteSource.Util.bytes(salt_byte), getName());
}
ShiroUser loginUser = shiroUserService.findUserByLoginName(loginName);
if (loginUser == null) {
throw new UnknownAccountException();
}
if (loginUser.isDisabled()) {
throw new DisabledAccountException();
}
ByteSource salt = ByteSource.Util.bytes(shiroUserService.getSaltBytes(loginUser));
return new SimpleAuthenticationInfo(loginName, loginUser.getPassword(), salt, getName());
}
}
3、用postman访问:http://localhost:10001/app/weicheLogin 参数 : {"custNum":"413185410"}
@RequestMapping(value = Urls.WCLOGIN, method = RequestMethod.POST)
@ResponseBody
public WcCustomerIsExistResponse wcLogin(@Valid @RequestBody WcCustomerIsExistRequest request, BindingResult result, Locale locale) {
WcCustomerIsExistResponse response = new WcCustomerIsExistResponse();
if (result.hasErrors()) {
Validators.addParameterErrors(response, result, messageSource, locale);
return response;
}
try {
mobileService.bindSubject(request.getSessionId());
response = messageMMSService.wcCustomerIsExist(request, response);
mobileService.serviceForNoAuthcForm(request.getCustNum(), request, response);
} catch (RemoteConnectFailureException e) {
LOG.error(e.getMessage(), e);
response = mockMessageMMSService.wcCustomerIsExist(request, response);
} catch (ParameterException e) {
Validators.addParameterErrors(response, e.getMessage(), messageSource, locale);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
response.error();
}
return response;
}
4、
@Override
public RESPONSE serviceForNoAuthcForm(
String loginName, REQUEST request, RESPONSE response) {
try {
String sessionId = loginNoPassword(loginName).toString();
request.setSessionId(sessionId);
response.setSessionId(sessionId);
} catch (UnknownAccountException e) {
LOG.debug(e.getMessage(), e);
response.error(MessageError.ERROR_AUTH);
} catch (DisabledAccountException e) {
LOG.debug(e.getMessage(), e);
response.error(MessageError.ERROR_DISABLED);
} catch (IncorrectCredentialsException e) {
LOG.debug(e.getMessage(), e);
response.error(MessageError.ERROR_AUTH);
} catch (ExcessiveAttemptsException e) {
LOG.debug(e.getMessage(), e);
response.error(MessageError.ERROR_ATTEMPTS);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
response.error();
}
return response;
}
private Serializable loginNoPassword(String loginName) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
UsernameNoPasswordCaptchaToken token=new UsernameNoPasswordCaptchaToken(loginName,null,null);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
} catch (InvalidSessionException e) {
subject.logout();
//subject.login(token);
}
Session session = subject.getSession();
Serializable sessionId = session.getId();
LOG.debug("Session with id [{}] startTimestamp:{}", sessionId, session.getStartTimestamp().getTime());
try {
Thread.sleep(100);
} catch (Exception ignored) {
}
session.touch();
LOG.debug("Session with id [{}] lastAccessTime:{}", sessionId, session.getLastAccessTime().getTime());
processConcurrentSessions();
return sessionId;
}
5、
import org.apache.shiro.authc.UsernamePasswordToken;
public class UsernameNoPasswordCaptchaToken extends UsernamePasswordToken {
private static final long serialVersionUID = 1L;
private String username;
public UsernameNoPasswordCaptchaToken(String username,String password,String host) {
super(username,password,host);
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
二、多个realm
1、
2、
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class QggRealm extends AuthorizingRealm{
@Autowired
@Qualifier("qggUserService")
private ShiroUserService shiroUserService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
// TODO Auto-generated method stub
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
String loginName = ((CustomizedToken) authcToken).getUsername();
ShiroUser loginUser = shiroUserService.findUserByLoginName(loginName);
if (loginUser == null) {
throw new UnknownAccountException();
}
if (loginUser.isDisabled()) {
throw new DisabledAccountException();
}
ByteSource salt = ByteSource.Util.bytes(shiroUserService.getSaltBytes(loginUser));
return new SimpleAuthenticationInfo(loginName, loginUser.getPassword(), salt, getName());
}
}
2、
public class WeicheRealm extends AuthorizingRealm{
@Autowired
@Qualifier("weicheUserService")
private ShiroUserService shiroUserService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
// TODO Auto-generated method stub
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
String loginName = ((CustomizedToken) authcToken).getUsername();
ShiroUser loginUser = shiroUserService.findUserByLoginName(loginName);
if (loginUser == null) {
throw new UnknownAccountException();
}
if (loginUser.isDisabled()) {
throw new DisabledAccountException();
}
ByteSource salt = ByteSource.Util.bytes(shiroUserService.getSaltBytes(loginUser));
return new SimpleAuthenticationInfo(loginName, loginUser.getPassword(), salt, getName());
}
}
3、入参WcCustomerIsExistRequest 类中包括登录名 custNum和登录类型loginType (Qgg和Weiche)
用postman访问:http://localhost:10001/app/mutilRealmLogin 参数 : {"custNum":"413185410","loginType":"Weiche"}
//
@RequestMapping(value = Urls.MUTILREALMLOGIN, method = RequestMethod.POST)
@ResponseBody
public WcCustomerIsExistResponse mutiRealmLogin(@Valid @RequestBody WcCustomerIsExistRequest request, BindingResult result, Locale locale) {
WcCustomerIsExistResponse response = new WcCustomerIsExistResponse();
if (result.hasErrors()) {
Validators.addParameterErrors(response, result, messageSource, locale);
return response;
}
try {
mobileService.bindSubject(request.getSessionId());
mobileService.serviceForMultiRealmAuthcForm(request.getCustNum(),request.getLoginType(), request, response);
} catch (RemoteConnectFailureException e) {
response = mockMessageMMSService.wcCustomerIsExist(request, response);
} catch (ParameterException e) {
Validators.addParameterErrors(response, e.getMessage(), messageSource, locale);
} catch (Exception e) {
response.error();
}
return response;
}
4、
@Override
public RESPONSE serviceForMultiRealmAuthcForm(
String loginName, String loginType, REQUEST request, RESPONSE response) {
try {
String sessionId = loginMultiRealm(loginName,loginType).toString();
request.setSessionId(sessionId);
response.setSessionId(sessionId);
} catch (UnknownAccountException e) {
LOG.debug(e.getMessage(), e);
response.error(MessageError.ERROR_AUTH);
} catch (DisabledAccountException e) {
LOG.debug(e.getMessage(), e);
response.error(MessageError.ERROR_DISABLED);
} catch (IncorrectCredentialsException e) {
LOG.debug(e.getMessage(), e);
response.error(MessageError.ERROR_AUTH);
} catch (ExcessiveAttemptsException e) {
LOG.debug(e.getMessage(), e);
response.error(MessageError.ERROR_ATTEMPTS);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
response.error();
}
return response;
}
5、
private Serializable loginMultiRealm(String loginName,String loginType) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
CustomizedToken token=new CustomizedToken(loginName,null,loginType);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
} catch (InvalidSessionException e) {
subject.logout();
//subject.login(token);
}
Session session = subject.getSession();
Serializable sessionId = session.getId();
LOG.debug("Session with id [{}] startTimestamp:{}", sessionId, session.getStartTimestamp().getTime());
try {
Thread.sleep(100);
} catch (Exception ignored) {
}
session.touch();
LOG.debug("Session with id [{}] lastAccessTime:{}", sessionId, session.getLastAccessTime().getTime());
processConcurrentSessions();
return sessionId;
}
6、
import org.apache.shiro.authc.UsernamePasswordToken;
public class CustomizedToken extends UsernamePasswordToken {
//登录类型,判断是哪种用户登录
private String loginType;
public CustomizedToken(final String username, final String password,String loginType) {
super(username,password);
this.loginType = loginType;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
}
7、重要:这个类选择使用哪个realm
import java.util.ArrayList;
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;
/**
* @author Alan_Xiang
* 自定义Authenticator
* 注意,当需要分别定义处理普通用户和管理员验证的Realm时,对应Realm的全类名应该包含字符串“User”,或者“Admin”。
* 并且,他们不能相互包含,例如,处理普通用户验证的Realm的全类名中不应该包含字符串"Admin"。
*/
public class CustomizedModularRealmAuthenticator extends ModularRealmAuthenticator {
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
throws AuthenticationException {
// 判断getRealms()是否返回为空
assertRealmsConfigured();
// 强制转换回自定义的CustomizedToken
CustomizedToken customizedToken = (CustomizedToken) authenticationToken;
// 登录类型
String loginType = customizedToken.getLoginType();
// 所有Realm
Collection realms = getRealms();
// 登录类型对应的所有Realm
Collection typeRealms = new ArrayList<>();
for (Realm realm : realms) {
if (realm.getName().contains(loginType))
typeRealms.add(realm);
}
// 判断是单Realm还是多Realm
if (typeRealms.size() == 1)
return doSingleRealmAuthentication(typeRealms.iterator().next(), customizedToken);
else
return doMultiRealmAuthentication(typeRealms, customizedToken);
}
}
参考:https://blog.csdn.net/xiangwanpeng/article/details/54802509