首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >app服务器端开发之用户登录(struts2)处理

app服务器端开发之用户登录(struts2)处理

作者头像
天涯泪小武
发布2019-01-17 10:51:50
1.2K0
发布2019-01-17 10:51:50
举报
文章被收录于专栏:SpringCloud专栏SpringCloud专栏

app基本都有用户注册登录相关的操作,那么登录后服务器需要做什么?当用户发起其他的请求时,怎么确定用户的唯一性,根据什么来把app用户和服务器关联起来?

之前问过一些新手,发现很多新手都是采用的如下方式:用户登录时,校验账号密码,如果正确的话,就给app返回一个用户表里对应该用户的UserId唯一标识,然后以后app的所以请求都带着这个UserId,这样去进行app和服务器的关联,通过UserId去做查询等相关的接口操作。进行操作时每次根据UserId去查询User,然后进行各项操作。但这样是对的吗?试想一下,如果是这样,通信关系靠的是一个UserId,那其实登录与否都不重要,只要知道id即可,任何的接口操作譬如获取所有订单啊、提现啊之类的靠这个id来维持。这肯定是有问题的,很多接口的操作在不登录的情况下是不能发起请求的,譬如一些紧要的订单信息,账号余额信息,付款请求等,但有一些接口是可以不登录就能访问的,譬如广告的banner,商品的展示等。如果靠UserId来进行关联,那怎么知道用户是否登录了呢,反正通过UserId就能进行所有的操作了。甚至有一些User表主键生成策略是int型递增,那这就更简单了,直接去猜个id,就能对用户进行所有的操作了,岂不很危险。

事实上,对服务器来说,登录操作是一个有特殊意义的操作,代表了一次会话。下面来回头看一下,http请求中的几个内置对象,page,request,session,application。这里用到的有request和session。request代表请求对象,session代表会话对象。request作用在一次请求的范围内,比如说你这次要干什么,要达到一个目的。你要传递一个东西就用request,只用一次。而session就是一次回话,它的销毁为session到期,默认30分钟,可以修改,还有关闭浏览器session也会销毁。session就可以理解为会话,好比你和一个人谈话,你们两个之间交流的信息可以多次交互。从这可以看出,app发起的每一次请求是一个request,你请求服务器,服务器给你回一次值。那么session就是用来做登录后这次会话的保存,一旦app登录了,那么在这次会话中这个用户就是有一个唯一的session_id的。这个是app用户和服务器交互的唯一标志,当退出登录,或者session过期后,这个用户和服务器的关联就停掉了。下次再登录,会再有一个新的session_id。

User表的唯一标识UserId是不应该暴露给app客户端的,关联应该是靠着session会话来进行。正确的操作应该是登录后,服务器保存这个session状态,在session里存入用户的唯一标志,然后把session_id返回给客户端,下次app带着session_id来进行请求。服务器对session进行校验,如果session还在,说明用户已经登录,如果不在,那说明已退出登录,或者session已经过期,这样就不至于暴露用户主键UserId,避免被刷接口。

下面来讲一下如何用struts来做这件事,struts自带拦截器interceptor,可以对某些请求进行拦截,某些不拦截。非常方便。

有些代码我就不贴完整的,那些配置我也不写了,就说一下大概的流程。

我们先来写一个拦截器,SessionInterceptor.java

package mobile.util.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts2.StrutsStatics;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

/**
 * 手机端校验是否登录的拦截器
 * 
 * @author wolf
 *
 */
public class SessionInterceptor implements Interceptor {

	private static final long serialVersionUID = -520686124206452492L;

	public void destroy() {

	}

	public void init() {

	}

	public String intercept(ActionInvocation invocation) throws Exception {
		ActionContext actionContext = invocation.getInvocationContext();
		HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
		HttpServletResponse response = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);
		
        HttpSession session = request.getSession();
		
		// 获取sessionid
		String sessionId = session.getId();
		response.addHeader("sessionId", sessionId);

		String actionName = invocation.getAction().toString();
		// 不拦截以下action
		if (actionName.contains("LoginAction") || actionName.contains("registRequestCode") || actionName.contains("verifyCode")) {
			return invocation.invoke();
		}
		
		// 没保存过用户session,说明没登录,或者session过期
		if (session.getAttribute(sessionId) == null) {
			// 用户未登录,跳到没登录处理的action
			session.invalidate();
			response.sendRedirect(request.getContextPath() + "/mobile/NoLoginAction.action");
			return null;
		}

		return invocation.invoke();
	}

}

在这个拦截器里,intetcept方法,能够获取到所有通过这个拦截器拦截的请求,并且做相应的处理,先获取session对象,把sessionId放到response的header里,将来返回给app用。然后有一些请求是在不登录的情况下也能访问的,不拦截的,这里我通过判断请求名字来确定哪些不需要拦截,譬如登录、注册、发验证码之类的,就不需要做拦截。其他的需要拦截的,就在session里获取attribute,来看这个sessionId里存的对象是否为空,如果空了,说明没登录或已超时,就跳转到没登录的处理去。不为空的话就invoke放行。那些session里存什么东西就由大家自行决定。在什么时候在session里存信息呢,当然是在登录成功后了。

下面是LoginAction的一段代码,里面有一些是自己的逻辑,不适用于别的项目,所以就看看思想就好了

public String logout() {
		HttpServletRequest request = ServletActionContext.getRequest();
		request.getSession().invalidate();
		
		BaseData basedata = new BaseData();
		basedata.setCode(1);
		PrintUtil.print(JsonUtil.toJsonString(basedata));
		
		return null;
	}
	
	/**
	 * 登录
	 * @return
	 */
	public String login() {
		OlderManReturnBean returnBean = new OlderManReturnBean();
		List<TbOlderMan> olderMans = olderManService.queryByName(userName);
		if(olderMans.size() == 0) {
			//用户不存在
			returnBean.setCode(-1);
		} else if(!olderMans.get(0).getLoginPwd().equals(MD5.toMD5(password))) {
			//密码错误
			returnBean.setCode(-2);
		} else {
			//登录成功
			returnBean.setCode(0);
			returnBean.setOlderMan(olderMans.get(0));
			HttpServletRequest request = ServletActionContext.getRequest();
			request.getSession().setAttribute(request.getSession().getId(), olderMans.get(0).getOlderManId());
			request.getSession().setMaxInactiveInterval(20);
		}
		PrintUtil.print(JsonUtil.toJsonString(returnBean));
		
		return null;
	}

登录成功后,在session里put进去值,key就是sessionId,value是用户的UserId。下次用户发请求时,带着sessionId来请求,我们就从session里getAttribute(sessionId),就能得到UserId了,如果得不到,说明已经不在登录状态了。通过拦截器来判断用户是否在登录状态,是否放行一些不需要登录就能访问的请求。

setMaxInactiveInterval方法是设置session超时时间,单位是秒,我这里设个20秒,方便测试session超时。可以通过浏览器来进行这个登录操作,登录成功后通过debug就能看到sessionId,然后随便输出个值。过20秒后,再试图访问一个其他的请求时,拦截器就会判断出该sessionId已经超时了,所以session去getAttribute时就会为空,然后就跳到没登录的action了。实际使用中请根据自己的需求来设置超时时间。

下面说一下struts.xml的配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
	<package name="mobile" namespace="/mobile" extends="json-default">

		<interceptors>
			<interceptor name="sessionInterceptor"
				class="mobile.util.interceptor.SessionInterceptor" />
			<interceptor-stack name="defaultStack">
				<interceptor-ref name="defaultStack" />
				<interceptor-ref name="sessionInterceptor" />
			</interceptor-stack>
		</interceptors>
		<default-interceptor-ref name="defaultStack" />

		<action name="NoLoginAction" class="mobile.action.base.NoLoginAction" />

		<!-- 登录、注册、修改密码 -->
		<action name="*LoginAction" class="mobile.action.LoginAction"
			method="{1}" />

		<action name="*SmsAction" class="mobile.action.SmsAction"
			method="{1}" />

		<action name="*OlderManAction" class="mobile.action.olderman.OlderManAction"
			method="{1}" />
		<action name="*UrgentPersonAction" class="mobile.action.olderman.UrgentPersonAction"
			method="{1}" />
	</package>
</struts>    

在这个package里,写明用的interceptor,将来所有这个namespace下的请求都会先走一遍拦截器,你可以通过写多个struts-*.xml,来区分不同的package,使用不同的拦截器,或者有的action不使用拦截器,这个就看自己的业务了。注意,struts是靠package来区分的,一个package下都会走这个拦截器。所以就有两种方式来决定是否拦截某些请求,一种就是在上面的拦截器代码里通过action的name来设置,还有就是通过有的package写拦截器,有的不写,或者两种方法同时使用来进行复杂的逻辑判断,拦截特定的请求。

以上就是app登录后,服务器端应该做的处理,和拦截器相关的处理。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年02月01日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档