您好,登录后才能下订单哦!
现有app上因功能扩展,需另外一部分用户登录,和原来的用户不在同一张表中。原来的shiro配置和单个realm不能满足多个表中用户(当然也可以在同一个realm中在两个表中查找,一个表查不到就去另一个表查,这种方式太笨了),所以自己尝试了以下扩展。实现不同的realm获取不同表中的用户。
一、先介绍一个用户时是怎么配置的
1、shiro.xml
<bean id="defaultSecurityManager" class="org.apache.shiro.mgt.DefaultSecurityManager"> <property name="realm" ref="shiroDbRealm" /> <property name="cacheManager" ref="shiroCacheManager" /> <property name="authenticator" ref="authenticator"></property> <property name="sessionManager" ref="defaultSessionManager"/> </bean> <bean id="shiroDbRealm" class="com.su.ShiroCaptchaDbRealm"> <!-- net.zkbc.shiro.AppConfig.hashedCredentialsMatcher() --> <property name="credentialsMatcher" ref="captchaCredentialsMatcher" /> </bean>
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 <REQUEST extends MobileRequest, RESPONSE extends MobileResponse> 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、
<bean id="defaultSecurityManager" class="org.apache.shiro.mgt.DefaultSecurityManager"> <!-- <property name="realm" ref="shiroDbRealm" /> --> <property name="cacheManager" ref="shiroCacheManager" /> <property name="authenticator" ref="authenticator"></property> <property name="realms"> <list> <ref bean="qggRealm" /> <ref bean="weicheRealm"/> </list> </property> <!-- net.zkbc.shiro.AppConfig.defaultSessionManager() --> <property name="sessionManager" ref="defaultSessionManager"/> </bean> <bean id="authenticator" class="net.zkbc.shiro.authc.CustomizedModularRealmAuthenticator"> <!-- 配置认证策略,只要有一个Realm认证成功即可,并且返回所有认证成功信息 --> <property name="authenticationStrategy"> <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean> </property> </bean> <bean id="qggRealm" class="net.zkbc.shiro.realm.QggRealm"> <!-- net.zkbc.shiro.AppConfig.hashedCredentialsMatcher() --> <property name="credentialsMatcher" ref="captchaCredentialsMatcher" /> </bean> <bean id="weicheRealm" class="net.zkbc.shiro.realm.WeicheRealm"> <!-- net.zkbc.shiro.AppConfig.hashedCredentialsMatcher() --> <property name="credentialsMatcher" ref="captchaCredentialsMatcher" /> </bean>
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 <REQUEST extends MobileRequest, RESPONSE extends MobileResponse> 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<Realm> realms = getRealms(); // 登录类型对应的所有Realm Collection<Realm> 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
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。