shiro多登录入口,多realm认证

1.XML配置
     
			/static/** = anon
			/error/** = anon
			/images/kaptcha.do* = anon				
			/login = authc
			/login* = authc
			/smart/** = authc
			/customLogin = authcstom
			/customLogin* = authcstom
			/custom/** = authcstom
			/** = role[*]
		
     

2.两个realm

public class CustomRealm extends AuthorizingRealm {

	protected AccountService accountService;

	/**
	 * 认证回调函数,登录时调用.
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		CaptchaUsernamePasswordToken token = (CaptchaUsernamePasswordToken) authcToken;
		doCaptchaValidate(token); 
		User user = accountService.findUserByLoginName(token.getUsername());
		if (user != null) {
			
			
			//2.判断是否有区,去掉该区的市、省编码
			if(StringUtils.isNotBlank(user.getRegion())){
				String[] resions = user.getRegion().split(",");
				Set resionsSet = new HashSet();   
				CollectionUtils.addAll(resionsSet, resions);
				resionsSet.remove("");
				if(StringUtils.isNotBlank(user.getCity())){
					String[] _citys = user.getCity().split(",");
					Set _citysSet = new HashSet();
					CollectionUtils.addAll(_citysSet, _citys);
					_citysSet.remove("");
					//循环遍历区 去掉多余市编码
					for (String resion : resionsSet) {
						String _resion = resion.substring(0,resion.length()-2)+"00";
						_citysSet.remove(_resion);
					}
					
					//重新保存
					user.setCity(StringUtils.join(_citysSet.toArray(),","));
				}
				
				user.setRegion(StringUtils.join(resionsSet.toArray(),","));
			}
			
			//处理省多余的逗号
			if(StringUtils.isNotBlank(user.getProvince())){
				String[] provinces = user.getProvince().split(",");
				Set provinceSet = new HashSet();
				CollectionUtils.addAll(provinceSet,provinces);
				provinceSet.remove("");
				user.setProvince(StringUtils.join(provinceSet.toArray(),","));
			}
			
			Session session  = SecurityUtils.getSubject().getSession();
		
			session.setAttribute("userInfo", user);
			byte[] salt = Encodes.decodeHex(user.getSalt());
			return new SimpleAuthenticationInfo(new ShiroUser(user.getId(),user.getLoginName(), user.getName()), user.getPassword(),ByteSource.Util.bytes(salt), getName());
		}else{
			throw new UnknownAccountException();
		}
	}

	/**
	 * 验证码校验
	 * 
	 * @param token
	 */
	protected void doCaptchaValidate(CaptchaUsernamePasswordToken token) {
		String captcha = (String) SecurityUtils
				.getSubject()
				.getSession()
				.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
		if (captcha != null && !captcha.equalsIgnoreCase(token.getCaptcha())) {
			throw new IncorrectCaptchaException("验证码错误");
		}
	}

	/**
	 * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		// 基于Permission的权限信息
		List pls = accountService.findUserPermission(shiroUser.id);
		for (Permission p : pls) {
			info.addStringPermission(p.getPerm());
		}
		// 基于Role的权限信息
		List rls = accountService.getRoleByUserId(shiroUser.id);
		for(Role r : rls){
			info.addRole(r.getName());
		}
		return info;
	}

	/**
	 * 设定Password校验的Hash算法与迭代次数.
	 */
	@PostConstruct
	public void initCredentialsMatcher() {
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(
				AccountService.HASH_ALGORITHM);
		matcher.setHashIterations(AccountService.HASH_INTERATIONS);

		setCredentialsMatcher(matcher);
	}

	@Autowired
	public void setAccountService(AccountService accountService) {
		this.accountService = accountService;
	}

	/**
	 * 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息.
	 */
	public static class ShiroUser implements Serializable {
		private static final long serialVersionUID = -1373760761780840081L;
		public Long id;
		public String loginName;
		public String name;

		public ShiroUser(Long id, String loginName, String name) {
			this.id = id;
			this.loginName = loginName;
			this.name = name;
		}

		public String getName() {
			return name;
		}

		public String getLoginName() {
			return loginName;
		}

		/**
		 * 本函数输出将作为默认的输出.
		 */
		@Override
		public String toString() {
			return loginName;
		}

		/**
		 * 重载hashCode,只计算loginName;
		 */
		@Override
		public int hashCode() {
			return Objects.hashCode(loginName);
		}

		/**
		 * 重载equals,只计算loginName;
		 */
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			ShiroUser other = (ShiroUser) obj;
			if (loginName == null) {
				if (other.loginName != null)
					return false;
			} else if (!loginName.equals(other.loginName))
				return false;
			return true;
		}
	}

}
empty
3.两个filter

public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {

    // 日志组件
    private Logger LOG = LoggerFactory.getLogger(CustomFormAuthenticationFilter.class);

	public static final String DEFAULT_CAPTCHA_PARAM = "captcha";
	private String captchaParam = DEFAULT_CAPTCHA_PARAM;
	public static final String LOGIN_TYPE = LoginType.CUSTOM.toString();

	private int maxSessions = 1;
	private boolean errorIfMaximumExceeded = false;

	private static final OnceLoginSessionMgr onceLoginSessionMgr = OnceLoginSessionMgr.getInstance();

	/**
	 * fix relogin
	 * 拒绝二次登录或.
	 */
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		if(errorIfMaximumExceeded) {
			return rejectSecondLogin(token, subject, request, response);
		} else {
			return invalidBeforeLogin(token, subject, request, response);
		}
	}
	
	/**
	 * fix relogin
	 * 拒绝二次登录.
	 */
	private boolean rejectSecondLogin(AuthenticationToken token, Subject subject, ServletRequest request,ServletResponse response) throws Exception {
		String username = getUsername(request);
		if (onceLoginSessionMgr.getShiroSubject(username) != null) {
			/*SecurityUtils.getSubject().logout();
			throw new ReLoginException("账号已经登录,请勿重复登录");*/
		}
		onceLoginSessionMgr.addShiroSubject(username, SecurityUtils.getSubject());
		SecurityUtils.getSubject().getSession().setAttribute("username", username);
		return super.onLoginSuccess(token, subject, request, response);
	}
	
	/**
	 * fix relogin
	 * 注销上次登录, 让上次登录失效.
	 * 
	 * 注: 这里登出不能使用 Subject 的 logout, 将会导致异常.
	 * 需使用 session 的 stop 方法来登出.
	 * @author well
	 */
	private boolean invalidBeforeLogin(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		String username = getUsername(request);
		Subject existSubject = onceLoginSessionMgr.getShiroSubject(username);
		
		if (existSubject != null) {
			/*onceLoginSessionMgr.removeShiroSubject(username);
			existSubject.getSession().removeAttribute("username");
			existSubject.logout(); // fix: Tomcat7 Null Point Exception. logout use session stop
			existSubject.getSession().stop();*/
		}
        SecurityUtils.getSubject().getSession().setAttribute("username", username);
		onceLoginSessionMgr.addShiroSubject(username, SecurityUtils.getSubject());
		return super.onLoginSuccess(token, subject, request, response);
	}

    /**
     * 重写父类方法,主要是为了写登录失败日志,这里是密码失败日志
     *
     * @author zhaobing
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
                                     ServletRequest request, ServletResponse response) {
        String username = getUsername(request);
        SecurityUtils.getSubject().getSession().setAttribute("username", username);
        onceLoginSessionMgr.addShiroSubject(username, SecurityUtils.getSubject());
        return super.onLoginFailure(token,e,request,response);
    }

	@Override
	protected CaptchaUsernamePasswordToken createToken(ServletRequest request, ServletResponse response) {
		String username = getUsername(request);
		String password = getPassword(request);
		String captcha = getCaptcha(request);
		boolean rememberMe = isRememberMe(request);
		String host = getHost(request);

		return new CaptchaUsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha,LOGIN_TYPE);
	}

	public String getCaptchaParam() {
		return captchaParam;
	}

	public void setCaptchaParam(String captchaParam) {
		this.captchaParam = captchaParam;
	}

	protected String getCaptcha(ServletRequest request) {
		return WebUtils.getCleanParam(request, getCaptchaParam());
	}

	public int getMaxSessions() {
		return maxSessions;
	}

	public void setMaxSessions(int maxSessions) {
		this.maxSessions = maxSessions;
	}

	public boolean isErrorIfMaximumExceeded() {
		return errorIfMaximumExceeded;
	}

	public void setErrorIfMaximumExceeded(boolean errorIfMaximumExceeded) {
		this.errorIfMaximumExceeded = errorIfMaximumExceeded;
	}


    /**
     * 获取当前网络ip
     * @param req
     * @return
     */
    private String getIpAddr(ServletRequest req){
        HttpServletRequest request = (HttpServletRequest)req;
        String ipAddress = request.getHeader("x-forwarded-for");
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
                //根据网卡取本机配置的IP
                InetAddress inet=null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress= inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
            if(ipAddress.indexOf(",")>0){
                ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }
}
public class CaptchaFormAuthenticationFilter extends FormAuthenticationFilter {

    // 日志组件
    private Logger LOG = LoggerFactory.getLogger(CaptchaFormAuthenticationFilter.class);

	public static final String DEFAULT_CAPTCHA_PARAM = "captcha";
	private String captchaParam = DEFAULT_CAPTCHA_PARAM;
	public static final String LOGIN_TYPE = LoginType.SMART.toString();
	private int maxSessions = 1;
	private boolean errorIfMaximumExceeded = false;

	private static final OnceLoginSessionMgr onceLoginSessionMgr = OnceLoginSessionMgr.getInstance();

	/**
	 * fix relogin
	 * 拒绝二次登录或.
	 */
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		if(errorIfMaximumExceeded) {
			return rejectSecondLogin(token, subject, request, response);
		} else {
			return invalidBeforeLogin(token, subject, request, response);
		}
	}
	
	/**
	 * fix relogin
	 * 拒绝二次登录.
	 */
	private boolean rejectSecondLogin(AuthenticationToken token, Subject subject, ServletRequest request,ServletResponse response) throws Exception {
		String username = getUsername(request);
		if (onceLoginSessionMgr.getShiroSubject(username) != null) {
			/*SecurityUtils.getSubject().logout();
			throw new ReLoginException("账号已经登录,请勿重复登录");*/
		}
		onceLoginSessionMgr.addShiroSubject(username, SecurityUtils.getSubject());
		SecurityUtils.getSubject().getSession().setAttribute("username", username);
		return super.onLoginSuccess(token, subject, request, response);
	}
	
	/**
	 * fix relogin
	 * 注销上次登录, 让上次登录失效.
	 * 
	 * 注: 这里登出不能使用 Subject 的 logout, 将会导致异常.
	 * 需使用 session 的 stop 方法来登出.
	 * @author
	 */
	private boolean invalidBeforeLogin(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		String username = getUsername(request);
		Subject existSubject = onceLoginSessionMgr.getShiroSubject(username);
		
		if (existSubject != null) {
			/*onceLoginSessionMgr.removeShiroSubject(username);
			existSubject.getSession().removeAttribute("username");
			existSubject.logout(); // fix: Tomcat7 Null Point Exception. logout use session stop
			existSubject.getSession().stop();*/
		}
        SecurityUtils.getSubject().getSession().setAttribute("username", username);
		onceLoginSessionMgr.addShiroSubject(username, SecurityUtils.getSubject());
		return super.onLoginSuccess(token, subject, request, response);
	}

    /**
     * 重写父类方法,主要是为了写登录失败日志,这里是密码失败日志
     *
     * @author
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
                                     ServletRequest request, ServletResponse response) {
        String username = getUsername(request);
        SecurityUtils.getSubject().getSession().setAttribute("username", username);
        onceLoginSessionMgr.addShiroSubject(username, SecurityUtils.getSubject());
        return super.onLoginFailure(token,e,request,response);
    }

	@Override
	protected CaptchaUsernamePasswordToken createToken(ServletRequest request, ServletResponse response) {
		String username = getUsername(request);
		String password = getPassword(request);
		String captcha = getCaptcha(request);
		boolean rememberMe = isRememberMe(request);
		String host = getHost(request);

		return new CaptchaUsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha,LOGIN_TYPE);
	}

	public String getCaptchaParam() {
		return captchaParam;
	}

	public void setCaptchaParam(String captchaParam) {
		this.captchaParam = captchaParam;
	}

	protected String getCaptcha(ServletRequest request) {
		return WebUtils.getCleanParam(request, getCaptchaParam());
	}

	public int getMaxSessions() {
		return maxSessions;
	}

	public void setMaxSessions(int maxSessions) {
		this.maxSessions = maxSessions;
	}

	public boolean isErrorIfMaximumExceeded() {
		return errorIfMaximumExceeded;
	}

	public void setErrorIfMaximumExceeded(boolean errorIfMaximumExceeded) {
		this.errorIfMaximumExceeded = errorIfMaximumExceeded;
	}


    /**
     * 获取当前网络ip
     * @param req
     * @return
     */
    private String getIpAddr(ServletRequest req){
        HttpServletRequest request = (HttpServletRequest)req;
        String ipAddress = request.getHeader("x-forwarded-for");
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
                //根据网卡取本机配置的IP
                InetAddress inet=null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress= inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
            if(ipAddress.indexOf(",")>0){
                ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }
}

4.登录类型区分

public enum LoginType {
	CUSTOM("Custom"),  SMART("Smart");

    private String type;

    private LoginType(String type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return this.type.toString();
    }
}
public class UsernamePasswordToken extends UsernamePasswordToken {

	private static final long serialVersionUID = -4007351673293500161L;
	
	private String captcha;
	private String ukeyId;
	private String loginType;

	public UsernamePasswordToken(String username, char[] password,
			boolean rememberMe, String host, String captcha, String ukeyId,String loginType) {
		super(username, password, rememberMe, host);
		this.captcha = captcha;
		this.ukeyId = ukeyId;
		this.loginType = loginType;
	}
	
	public String getLoginType() {
		return loginType;
	}

	public void setLoginType(String loginType) {
		this.loginType = loginType;
	}

	public UsernamePasswordToken(String username, char[] password,
			boolean rememberMe, String host, String captcha,String loginType) {
		this(username, password, rememberMe, host, captcha, null,loginType);
	}

	public String getCaptcha() {
		return captcha;
	}

	public void setCaptcha(String captcha) {
		this.captcha = captcha;
	}

	public String getUkeyId() {
		return ukeyId;
	}

	public void setUkeyId(String ukeyId) {
		this.ukeyId = ukeyId;
	}
}
public class CustomizedModularRealmAuthenticator extends ModularRealmAuthenticator{
	@Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        // 判断getRealms()是否返回为空
        assertRealmsConfigured();
        // 强制转换回自定义的CustomizedToken
        CaptchaUsernamePasswordToken customizedToken = (CaptchaUsernamePasswordToken) 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);
    }
}

5.controller

@Controller
@RequestMapping(value = "/customLogin")
public class CustomLoginController {

	@RequestMapping(method = RequestMethod.GET)
	public String customLogin() {
		// 如果已登录,直接跳转至主页
		ShiroUser user = (ShiroUser) SecurityUtils.getSubject().getPrincipal();
		if (user != null) {
			return "redirect:custom/main/index";
		}
		return "account/customLogin";
	}

	@RequestMapping(method = RequestMethod.POST)
	public String fail(@RequestParam(FormAuthenticationFilter.DEFAULT_USERNAME_PARAM) String userName, Model model) {
		model.addAttribute(FormAuthenticationFilter.DEFAULT_USERNAME_PARAM, userName);
		return "account/customLogin";
	}
}
@Controller
@RequestMapping(value = "/smartLogin")
public class SmartLoginController {

	@RequestMapping(method = RequestMethod.GET)
	public String smartLogin() {
		// 如果已登录,直接跳转至主页
		ShiroUser user = (ShiroUser) SecurityUtils.getSubject().getPrincipal();
		if (user != null) {
			return "redirect:smart/main/index";
		}
		return "account/login";
	}

	@RequestMapping(method = RequestMethod.POST)
	public String fail(@RequestParam(FormAuthenticationFilter.DEFAULT_USERNAME_PARAM) String userName, Model model) {
		model.addAttribute(FormAuthenticationFilter.DEFAULT_USERNAME_PARAM, userName);
		return "account/login";
	}
}

你可能感兴趣的:(shiro多登录入口,多realm认证)