Shiro realizes multiple realm login for different users

Due to the expansion of functions on the existing app, another part of the user needs to be logged in, which is not in the same table as the original user. The original shiro configuration and a single realm cannot satisfy users in multiple tables (of course, it is also possible to search in two tables in the same realm, and if one table cannot be found, go to the other table, this method is too stupid), So I tried the following extension myself. Implement different realms to get users in different tables.

1. How to configure a user first

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 is related to getting the verification code
			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. Access with postman: http://localhost:10001/app/weicheLogin   Parameters: {"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;
	}
	

}

2. Multiple 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">
        <!-- Configure the authentication policy, as long as one Realm is successfully authenticated, and return all authentication success information -->
        <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. The WcCustomerIsExistRequest class includes the login name custNum and the login type loginType (Qgg and 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 {

    //Login type, determine which user is logged in
    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. Important: This class chooses which realm to use

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 
 * Custom Authenticator
 * Note that when you need to define the Realm that handles the authentication of ordinary users and administrators, the full class name of the corresponding Realm should contain the string "User", or "Admin".
 * Also, they cannot contain each other, for example, the full class name of Realm that handles normal user authentication should not contain the string "Admin".
 */
public class CustomizedModularRealmAuthenticator extends ModularRealmAuthenticator {

    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        // Determine if getRealms() returns empty
        assertRealmsConfigured();
        // Cast back to custom CustomizedToken
        CustomizedToken customizedToken = (CustomizedToken) authenticationToken;
        // login type
        String loginType = customizedToken.getLoginType();
        // all Realms
        Collection<Realm> realms = getRealms();
        // All Realms corresponding to the login type
        Collection<Realm> typeRealms = new ArrayList<>();
        for (Realm realm : realms) {
            if (realm.getName().contains(loginType))
                typeRealms.add(realm);
        }

        // Determine whether it is a single Realm or multiple Realm
        if (typeRealms.size() == 1)
            return doSingleRealmAuthentication(typeRealms.iterator().next(), customizedToken);
        else
            return doMultiRealmAuthentication(typeRealms, customizedToken);
    }

}

Reference: https://blog.csdn.net/xiangwanpeng/article/details/54802509

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324481259&siteId=291194637