Jeesite 登录逻辑分析

最近项目需求研究免登录进入jeesite系统,于是对jeesite的登录逻辑进行了研究。

一:当用户从url访问jeesite系统时,首先会通过下面方法。

@RequestMapping(value = "${adminPath}/login", method = RequestMethod.GET)
	public String login(HttpServletRequest request, HttpServletResponse response, Model model) {
		Principal principal = UserUtils.getPrincipal();
 
//		// 默认页签模式
//		String tabmode = CookieUtils.getCookie(request, "tabmode");
//		if (tabmode == null){
//			CookieUtils.setCookie(response, "tabmode", "1");
//		}
		
		if (logger.isDebugEnabled()){
			logger.debug("login, active session size: {}", sessionDAO.getActiveSessions(false).size());
		}
		
		// 如果已登录,再次访问主页,则退出原账号。
		if (Global.TRUE.equals(Global.getConfig("notAllowRefreshIndex"))){
			CookieUtils.setCookie(response, "LOGINED", "false");
		}
		
		// 如果已经登录,则跳转到管理首页
		if(principal != null && !principal.isMobileLogin()){
			return "redirect:" + adminPath;
		}
		return "modules/sys/sysLogin";
	}

public static Principal getPrincipal(){
		try{
			Subject subject = SecurityUtils.getSubject();
			Principal principal = (Principal)subject.getPrincipal();
			if (principal != null){
				return principal;
			}
//			subject.logout();
		}catch (UnavailableSecurityManagerException e) {
			
		}catch (InvalidSessionException e){
			
		}
		return null;
	}

public static Subject getSubject() {
 
		/*  54*/ Subject subject = ThreadContext.getSubject();
		/*  55*/ if (subject == null) {
			/*  56*/ subject = (new org.apache.shiro.subject.Subject.Builder()).buildSubject();
			/*  57*/ ThreadContext.bind(subject);
		}
		/*  59*/ return subject;
	}

首先从UserUtils获取Principal,然后Principal是由Subject强转而来的,Subject由SecurityUtils获取的,具体代码在上面,当系统登录成功的时候ThreadContext会存储Subject信息,所以上面才可以获取到。当登录超时或未登录时,ThreadContext获取不到Subject会重新新建个subject并bind到ThreadContext中。系统根据Principal中的信息判断当前用户是否登录过。

二:当用户从登录页面进行登录时
jeeste登录页面是sysLogin.jsp。主要就是个form表单提交

<form id="loginForm" class="form-signin" action="${ctx}/login" method="post">

在spring-context-shiro.xml配置了安全认证过滤器执行formAuthenticationFilter

<!-- 安全认证过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" /><!-- 
		<property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" /> -->
		<property name="loginUrl" value="${adminPath}/login" />
		<property name="successUrl" value="${adminPath}?login" />
		<property name="filters">
            <map>
                <!-- <entry key="cas" value-ref="casFilter"/> -->
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>
		<property name="filterChainDefinitions">
			<ref bean="shiroFilterChainDefinitions"/>
		</property>
	</bean>

根据请求信息创建token然后传给SystemAuthorizingRealm中的doGetAuthenticationInfo进行认证

protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
		String username = getUsername(request);
		String password = getPassword(request);
		if (password==null){
			password = "";
		}
		boolean rememberMe = isRememberMe(request);
		String host = StringUtils.getRemoteAddr((HttpServletRequest)request);
		String captcha = getCaptcha(request);
		boolean mobile = isMobileLogin(request);
		return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha, mobile);
	}

/**
	 * 认证回调函数, 登录时调用
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) {
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		
		int activeSessionSize = getSystemService().getSessionDao().getActiveSessions(false).size();
		if (logger.isDebugEnabled()){
			logger.debug("login submit, active session size: {}, username: {}", activeSessionSize, token.getUsername());
		}
		
		// 校验登录验证码
		if (LoginController.isValidateCodeLogin(token.getUsername(), false, false)){
			Session session = UserUtils.getSession();
			String code = (String)session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);
			if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)){
				throw new AuthenticationException("msg:验证码错误, 请重试.");
			}
		}
		
		// 校验用户名密码
		User user = getSystemService().getUserByLoginName(token.getUsername());
		if (user != null) {
			if (Global.NO.equals(user.getLoginFlag())){
				throw new AuthenticationException("msg:该已帐号禁止登录.");
			}
			byte[] salt = Encodes.decodeHex(user.getPassword().substring(0,16));
			return new SimpleAuthenticationInfo(new Principal(user, token.isMobileLogin()), 
					user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());
		} else {
			return null;
		}
	}

doGetAuthenticationInfo会根据token中的信息生成SimpleAuthenticationInfo,交给info认证。

return new SimpleAuthenticationInfo(new Principal(user, token.isMobileLogin()), 
					user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());
public void merge(AuthenticationInfo info) {
 
		/* 194*/ if (info == null || info.getPrincipals() == null || info.getPrincipals().isEmpty())
			/* 195*/ return;
 
		/* 198*/ if (principals == null) {
			/* 199*/ principals = info.getPrincipals();
		} else {
			/* 201*/ if (!(principals instanceof MutablePrincipalCollection))
				/* 202*/ principals = new SimplePrincipalCollection(principals);
 
			/* 204*/ ((MutablePrincipalCollection) principals).addAll(info.getPrincipals());
		}
 
		/* 213*/ if (credentialsSalt == null && (info instanceof SaltedAuthenticationInfo))
			/* 214*/ credentialsSalt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
 
		/* 217*/ Object thisCredentials = getCredentials();
		/* 218*/ Object otherCredentials = info.getCredentials();
 
		/* 220*/ if (otherCredentials == null)
			/* 221*/ return;
 
		/* 224*/ if (thisCredentials == null) {
			/* 225*/ credentials = otherCredentials;
			/* 226*/ return;
		}
 
		/* 229*/ if (!(thisCredentials instanceof Collection)) {
			/* 230*/ Set newSet = new HashSet();
			/* 231*/ newSet.add(thisCredentials);
			/* 232*/ setCredentials(newSet);
		}
 
		/* 236*/ Collection credentialCollection = (Collection) getCredentials();
		/* 237*/ if (otherCredentials instanceof Collection)
			/* 238*/ credentialCollection.addAll((Collection) otherCredentials);
 
		/* 240*/ else
			/* 240*/ credentialCollection.add(otherCredentials);
	}
 
	public boolean equals(Object o) {
 
		/* 253*/ if (this == o)
			/* 253*/ return true;
		/* 254*/ if (!(o instanceof SimpleAuthenticationInfo))
			/* 254*/ return false;
 
		/* 256*/ SimpleAuthenticationInfo that = (SimpleAuthenticationInfo) o;
 
		/* 259*/ return principals == null ? that.principals == null : principals.equals(that.principals);
	}

如果认证失败会执行LoginController中的loginFail方法,并且返回登录页面

/**
	 * 登录失败,真正登录的POST请求由Filter完成
	 */
	@RequestMapping(value = "${adminPath}/login", method = RequestMethod.POST)
	public String loginFail(HttpServletRequest request, HttpServletResponse response, Model model) {
		Principal principal = UserUtils.getPrincipal();
		
		// 如果已经登录,则跳转到管理首页
		if(principal != null){
			return "redirect:" + adminPath;
		}
 
		String username = WebUtils.getCleanParam(request, FormAuthenticationFilter.DEFAULT_USERNAME_PARAM);
		boolean rememberMe = WebUtils.isTrue(request, FormAuthenticationFilter.DEFAULT_REMEMBER_ME_PARAM);
		boolean mobile = WebUtils.isTrue(request, FormAuthenticationFilter.DEFAULT_MOBILE_PARAM);
		String exception = (String)request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
		String message = (String)request.getAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM);
		
		if (StringUtils.isBlank(message) || StringUtils.equals(message, "null")){
			message = "用户或密码错误, 请重试.";
		}
 
		model.addAttribute(FormAuthenticationFilter.DEFAULT_USERNAME_PARAM, username);
		model.addAttribute(FormAuthenticationFilter.DEFAULT_REMEMBER_ME_PARAM, rememberMe);
		model.addAttribute(FormAuthenticationFilter.DEFAULT_MOBILE_PARAM, mobile);
		model.addAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME, exception);
		model.addAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM, message);
		
		if (logger.isDebugEnabled()){
			logger.debug("login fail, active session size: {}, message: {}, exception: {}", 
					sessionDAO.getActiveSessions(false).size(), message, exception);
		}
		
		// 非授权异常,登录失败,验证码加1。
		if (!UnauthorizedException.class.getName().equals(exception)){
			model.addAttribute("isValidateCodeLogin", isValidateCodeLogin(username, true, false));
		}
		
		// 验证失败清空验证码
		request.getSession().setAttribute(ValidateCodeServlet.VALIDATE_CODE, IdGen.uuid());
		
		// 如果是手机登录,则返回JSON字符串
		if (mobile){
	        return renderString(response, model);
		}
		
		return "modules/sys/sysLogin";
	}

如果登录成功会进行LoginController中的index方法,进入管理页面。

@RequiresPermissions("user")
	@RequestMapping(value = "${adminPath}")
	public String index(HttpServletRequest request, HttpServletResponse response) {
		Principal principal = UserUtils.getPrincipal();
 
		// 登录成功后,验证码计算器清零
		isValidateCodeLogin(principal.getLoginName(), false, true);
		if (logger.isDebugEnabled()){
			logger.debug("show index, active session size: {}", sessionDAO.getActiveSessions(false).size());
		}
		
		// 如果已登录,再次访问主页,则退出原账号。
		if (Global.TRUE.equals(Global.getConfig("notAllowRefreshIndex"))){
			String logined = CookieUtils.getCookie(request, "LOGINED");
			if (StringUtils.isBlank(logined) || "false".equals(logined)){
				CookieUtils.setCookie(response, "LOGINED", "true");
			}else if (StringUtils.equals(logined, "true")){
				UserUtils.getSubject().logout();
				return "redirect:" + adminPath + "/login";
			}
		}
		
		// 如果是手机登录,则返回JSON字符串
		if (principal.isMobileLogin()){
			if (request.getParameter("login") != null){
				return renderString(response, principal);
			}
			if (request.getParameter("index") != null){
				return "modules/sys/sysIndex";
			}
			return "redirect:" + adminPath + "/login";
		}
		
//		// 登录成功后,获取上次登录的当前站点ID
//		UserUtils.putCache("siteId", StringUtils.toLong(CookieUtils.getCookie(request, "siteId")));
 
//		System.out.println("==========================a");
//		try {
//			byte[] bytes = com.thinkgem.jeesite.common.utils.FileUtils.readFileToByteArray(
//					com.thinkgem.jeesite.common.utils.FileUtils.getFile("c:\\sxt.dmp"));
//			UserUtils.getSession().setAttribute("kkk", bytes);
//			UserUtils.getSession().setAttribute("kkk2", bytes);
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
		for (int i=0; i<1000000; i++){
			//UserUtils.getSession().setAttribute("a", "a");
			request.getSession().setAttribute("aaa", "aa");
		}
//		System.out.println("==========================b");
		return "modules/sys/sysIndex";
	}

还有些ssm的内容大家应该都知道。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值