shiro實現不同用戶多realm登錄
阿新 • • 發佈:2018-04-16
shiro 多realm 現有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
shiro實現不同用戶多realm登錄