taotao用户登录springMVC拦截器的实现
在springMVC中写拦截器,只需要两步:
一、写 java 拦截器类,实现 interceptor 拦截器接口。
二、在 springMVC 的xml配置文件中,配置我们创建的拦截器对象及其拦截目标 url
首先,在前台门户项目中创建一个拦截器java类:
package com.taotao.portal.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.taotao.common.utils.CookieUtils; import com.taotao.pojo.TbUser; import com.taotao.portal.service.impl.UserServiceImpl; public class LoginInterceptor implements HandlerInterceptor { /** * 注意,因为拦截器属于Controller,而在Controller中是不能使用Value注解来获取配置文件中设置的值的, * 因为 Value是spring父容器加载的属性,而Controller是springMVC子容器加载的,子容器可以访问父容器中的对象, * 但是不能访问父容器中的属性 * * 而我们在这里还需要用到 配置文件 中的 url值,只要用变通的方法 * 1.首先在service的实现类 UserServiceImpl 中用@Value获取到属性,并将其设置成 public类型 * 2.在Controller这里,不注入接口,而是直接注入 UserServiceImpl 实现类 * 这样就可以在Controller中直接使用注入的service实现类的属性了 */ @Autowired private UserServiceImpl userService; //在handler执行之前处理 //返回值决定handler是否执行。true:执行, false:不执行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //判断用户是否登录 //从cookie中取token String token = CookieUtils.getCookieValue(request, "TT_TOKEN"); //根据token换取用户信息,调用sso系统的接口 ///这里需要写一些业务 逻辑,不要在拦截器中写,单写一个service,只在这里注入并调用 TbUser user = userService.getUserByToken(token); //如果取不到用户信息 if (user==null) { //跳转到登录页面,把用户请求的url作为参数传递给登录页面 response.sendRedirect(userService.SSO_BASE_URL+userService.SSO_PAGE_LOGIN +"?redirect="+request.getRequestURL()); //返回false return false; } //如果取到用户信息,放行
//把用户信息放到request中,以便后面在Controller中使用用户信息
request.setAttribute("user", user); //request中可以直接存对象,只要到了后台都可以直接存对象 return true; } //在 handler执行之后,返回ModelAndView之前处理 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub } //返回ModelAndView之后处理(响应用户之后,可以用来处理异常) @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub } }
只要实现拦截器接口,覆盖其中的方法即可。
在方法中,我们需要写一些业务逻辑,这里其实就是用 从 cookie 中取出的 token,到redis中查询,看是否能查到用户,若能查到,说明已经登录,拦截器不拦截,继续往下访问;若查询不到,说明当前用户登录已经过期,需要携带目标 url 跳转到登录页面(以便登录完可以继续访问之前的目标 url)。
其中用到的 service:
package com.taotao.portal.service.impl; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.taotao.common.pojo.TaotaoResult; import com.taotao.common.utils.HttpClientUtil; import com.taotao.pojo.TbUser; import com.taotao.portal.service.UserService; @Service public class UserServiceImpl implements UserService { @Value("${SSO_BASE_URL}") public String SSO_BASE_URL;//#单点登录系统的url SSO_BASE_URL=http://localhost:8084 @Value("${SSO_USER_TOKEN}") private String SSO_USER_TOKEN; //#根据token获取用户信息的url user/token/ @Value("${SSO_PAGE_LOGIN}") public String SSO_PAGE_LOGIN; //#单点登录系统登录url /page/login/ @Value("${SSO_USER_LOGOUT}") public String SSO_USER_LOGOUT; //#退出登录 SSO_USER_LOGOUT=/user/logout/ @Override public TbUser getUserByToken(String token) { try { //调用sso接口,根据token获取用户信息 String json = HttpClientUtil.doGet(SSO_BASE_URL+SSO_USER_TOKEN+token); TaotaoResult taotaoResult = TaotaoResult.formatToPojo(json, TbUser.class); if (taotaoResult.getStatus()==200) { TbUser user = (TbUser) taotaoResult.getData(); return user; } } catch (Exception e) { e.printStackTrace(); } //如果没有获取到user或者出现异常,都返回null return null; } }
写好拦截器java类后,需要在SpringMVC中配置我们自定义的拦截器:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.taotao.portal.controller" /> <mvc:annotation-driven /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <!-- 资源映射 因为此工程访问的是 *.html 所以不用配置静态资源映射 静态资源映射只有当web.xml中的servlet—mapping 中的url-pattern为 / 时才需要用 --> <!-- <mvc:resources location="/WEB-INF/css/" mapping="/css/**"/> <mvc:resources location="/WEB-INF/js/" mapping="/js/**"/> --> <!-- 定义文件上传解析器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设定默认编码 --> <property name="defaultEncoding" value="UTF-8"></property> <!-- 设定文件上传的最大值5MB,5*1024*1024 --> <property name="maxUploadSize" value="5242880"></property> </bean> <!-- 拦截器配置 --> <mvc:interceptors> <mvc:interceptor> <!-- 拦截订单类请求 --> <mvc:mapping path="/item/**"/> <bean class="com.taotao.portal.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors> </beans>
其中 path属性配置我们要拦截的请求路径:
<mvc:mapping path="/item/**"/>
这里我们拦截所有以 /item 开头的请求。
然后bean就是配置我们自定义的java类的全限定名:
<bean class="com.taotao.portal.interceptor.LoginInterceptor"/>
然后我们测试:
在门户首页搜索 商品
因为请求不是 item 开头的,所以拦截器不拦截,然后我们点击我们搜索到的商品,发送查看商品信息请求:
因为这回的请求是 以 /item 开头的,所以被拦截器拦截,拦截器检查到当前 的 token 没有在redis中查到对应用户,所以判断为未登录,跳转到登录页
http://localhost:8084/page/login/
并且携带了原 url 作为参数 redirect=http://localhost:8082/item/153984258656220.html
然后,在跳转到登录页请求的Controller中,又将 参数 redirect 取出,放到 model域中,存到 jsp 页面上
sso中的 PageController 中的登录页面跳转方法:
/** * 跳转到登录页面 * @param redirect 登录成功后跳转的回调页面url * @param model * @return */ @RequestMapping("/login") public String showLogin(String redirect,Model model) { model.addAttribute("redirect", redirect); return "login"; }
前台 login.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page trimDirectiveWhitespaces="true" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"/> <title>登录淘淘</title> <link type="text/css" rel="stylesheet" href="/css/login.css"/> <script type="text/javascript" src="/js/jquery-1.6.4.js"></script> </head> <body> <div class="w"> <div id="logo"> <a href="http://localhost:8082" clstag="passport|keycount|login|01"> <img src="/images/taotao-logo.gif" alt="淘淘" width="170" height="60"/> </a><b></b> </div> </div> <form id="formlogin" method="post" onsubmit="return false;"> <div class=" w1" id="entry"> <div class="mc " id="bgDiv"> <div id="entry-bg" clstag="passport|keycount|login|02" style="width: 511px; height: 455px; position: absolute; left: -44px; top: -44px; background: url(/images/544a11d3Na5a3d566.png) 0px 0px no-repeat;"> </div> <div class="form "> <div class="item fore1"> <span>用户名</span> <div class="item-ifo"> <input type="text" id="loginname" name="username" class="text" tabindex="1" autocomplete="off"/> <div class="i-name ico"></div> <label id="loginname_succeed" class="blank invisible"></label> <label id="loginname_error" class="hide"><b></b></label> </div> </div> <script type="text/javascript"> setTimeout(function () { if (!$("#loginname").val()) { $("#loginname").get(0).focus(); } }, 0); </script> <div id="capslock"><i></i><s></s>键盘大写锁定已打开,请注意大小写</div> <div class="item fore2"> <span>密码</span> <div class="item-ifo"> <input type="password" id="nloginpwd" name="password" class="text" tabindex="2" autocomplete="off"/> <div class="i-pass ico"></div> <label id="loginpwd_succeed" class="blank invisible"></label> <label id="loginpwd_error" class="hide"></label> </div> </div> <div class="item login-btn2013"> <input type="button" class="btn-img btn-entry" id="loginsubmit" value="登录" tabindex="8" clstag="passport|keycount|login|06"/> </div> </div> </div> <div class="free-regist"> <span><a href="/page/register" clstag="passport|keycount|login|08">免费注册>></a></span> </div> </div> </form> <script type="text/javascript"> var redirectUrl = "${redirect}"; var LOGIN = { checkInput:function() { if ($("#loginname").val() == "") { alert("用户名不能为空"); $("#loginname").focus(); return false; } if ($("#nloginpwd").val() == "") { alert("密码不能为空"); $("#nloginpwd").focus(); return false; } return true; }, doLogin:function() { $.post("/user/login", $("#formlogin").serialize(),function(data){ if (data.status == 200) { alert("登录成功!"); if (redirectUrl == "") {//如果没有回调跳转到首页 location.href = "http://localhost:8082"; } else { //有回调,跳转到回调页面 location.href = redirectUrl; } } else { alert("登录失败,原因是:" + data.msg); $("#loginname").select(); } }); }, login:function() { if (this.checkInput()) { this.doLogin(); } } }; $(function(){ $("#loginsubmit").click(function(){ LOGIN.login(); }); }); </script> </body> </html>
前台填写好用户名、密码后,登录表单提交,如果登录成功,前台则会继续 跳转之前要访问的 目标 url。