我们上节课一起搭建了订单的服务工程和web工程,我们参考京东可以知道,京东在没有登录时就可以使用购物车,但是当要真正付款的时候,一定是要求登录的。也就是说由购物车列表页面直接跳转到登录页面去登录。这显然用到了拦截器的功能,这节课我们便一起实现登录功能。
下图便是购物车列表页面,我们点击"去结算",如果当前用户还没登录,是必须要先登录的。
下面我们便来写拦截器,拦截器是要实现HandlerInterceptor的,我们在taotao-order-web工程的src/main/java目录下新建一个包com.taotao.order.interceptor并在该包下新建LoginInterceptor拦截器(实现HandlerInterceptor),如下图所示。
LoginInterceptor所有代码如下:
package com.taotao.order.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.taotao.common.pojo.TaotaoResult;
import com.taotao.common.utils.CookieUtils;
import com.taotao.sso.service.UserService;
public class LoginInterceptor implements HandlerInterceptor {
@Value("${TOKEN_KEY}")
private String TOKEN_KEY;
@Value("${SSO_URL}")
private String SSO_URL;
@Autowired
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 执行的时机,在handler之前先执行此方法,拦截请求让用户登录就在这个方法拦截
//1.从cookie中取token信息
String token = CookieUtils.getCookieValue(request, TOKEN_KEY);
//2.如果取不到token,跳转到sso的登录页面,需要把当前请求的url做为参数传递给sso,sso登录成功之后跳转回请求的页面
if(StringUtils.isBlank(token)){
//取当前请求的url
String requestURL = request.getRequestURL().toString();
//跳转到登录页面,用redirect比较合适,登录之后还要回到当前页面,因此要在请求url中添加一个回调地址
response.sendRedirect(SSO_URL+"/page/login?url="+requestURL);
//既然没登录,肯定是要拦截的
return false;
}
//3.取到token,调用sso系统的服务判断用户是否登录,既然要调用SSO服务接口,就要依赖这个taotao-sso-interface
TaotaoResult result = userService.getUserByToken(token);
//4.如果用户未登录(有token,但是已经过期,也算是没登录),即没有取到用户信息。跳转到sso的登录页面
//返回的TaotaoResult如果没有登录的话,状态码是400,如果登录了的话,状态码是200
if(result.getStatus() != 200){
//取当前请求的url
String requestURL = request.getRequestURL().toString();
//跳转到登录页面,用redirect比较合适,登录之后还要回到当前页面,因此要在请求url中添加一个回调地址
response.sendRedirect(SSO_URL+"/page/login?url="+requestURL);
//既然没登录,肯定是要拦截的
return false;
}
//5.如果取到用户信息,就放行。
//返回值true表示放行,返回false表示拦截
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// handler执行之后,modelAndView返回之前,可以对返回值进行处理
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// 在ModelAndView返回之后,这时只能做些异常处理了
}
}
可以看到,我们在代码中用到了两个常量,常量我们要定义在配置文件当中,如下图所示
配置文件中内容如下:
#token在cookie中保存的key,这个key与我们SSO工程定义的key值是一样的
TOKEN_KEY=TT_TOKEN
#SSO系统的url
SSO_URL=http://localhost:8088
preHandle方法的第3步通过token取用户信息,显然用到了SSO服务的接口,因此我们需要在taotao-order-web工程依赖taotao-sso-interface工程,如下图所示。
依赖代码如下:
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-sso-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
光依赖taotao-sso-interface还不行,我们还得在springmvc.xml文件中引用taotao-sso-service发布的dubbo服务,另外我们写的拦截器Spring是不知道的,我们得告诉Spring我们写了这个拦截器,于是需要在springmvc.xml文件中配置下拦截器。如下图所示。
修改完后springmvc.xml文件的内容如下:
<?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:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:resource/resource.properties"/>
<!-- 配置注解驱动 -->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 配置包扫描器,扫描@Controller注解的类 -->
<context:component-scan base-package="com.taotao.order.controller"/>
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/order/**"/>
<bean class="com.taotao.order.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
<!-- 引用dubbo服务 -->
<dubbo:application name="taotao-order-web"/>
<dubbo:registry protocol="zookeeper" address="192.168.156.14:2181"/>
<dubbo:reference interface="com.taotao.sso.service.UserService" id="userService" />
</beans>
我们是要由购物车列表页面访问订单页面时触发拦截器的,因此我们要先处理购物车列表页面,如下图所示,我给大家提供的静态页面这里已经改过了,不用修改。
由于要访问订单页面,因此我们需要把订单的静态资源放到taotao-order-web工程下,大家可以到http://download.csdn.net/detail/u012453843/9872711这个地址下载相关静态资源文件。将css、images、js文件放到webapp目录下,把jsp放到WEB-INF目录下。
我们要访问的订单页面是order-cart.jsp页面,如下图所示,这个页面中"cartList"是Controller返回的购物车列表,cart的属性一定要正确,我给大家的静态资源文件已经修改好了,可以直接使用。
我们需要在taotao-order-web写一个Controller来响应购物车列表请求访问订单页面的请求,如下图所示。
OrderController代码如下:
package com.taotao.order.controller;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.alibaba.fastjson.JSON;
import com.taotao.common.utils.CookieUtils;
import com.taotao.pojo.TbItem;
@Controller
public class OrderController {
@Value("${CART_KEY}")
private String CART_KEY;
@RequestMapping("/order/order-cart")
public String showOrderCart(HttpServletRequest request){
//用户必须是登录状态
//取用户ID
//根据用户ID取收获地址列表,这里就使用静态数据了
//把收货地址列表取出传递给页面
//从cookie中取购物车商品列表展示到页面
List<TbItem> cartList = getCartItemList(request);
request.setAttribute("cartList", cartList);
//返回逻辑视图
return "order-cart";
}
private List<TbItem> getCartItemList(HttpServletRequest request){
//从cookie中取购物车商品列表
String json = CookieUtils.getCookieValue(request, CART_KEY, true);//为了防止乱码,统一下编码格式
if(StringUtils.isBlank(json)){
//说明cookie中没有商品列表,那么就返回一个空的列表
return new ArrayList<TbItem>();
}
List<TbItem> list = JSON.parseArray(json, TbItem.class);
return list;
}
}
上面代码中用到了常量CART_KEY,因此我们需要在配置文件中配置下这个常量,如下图所示。
当前配置文件的内容如下:
#token在cookie中保存的key,这个key与我们SSO工程定义的key值是一样的
TOKEN_KEY=TT_TOKEN
#SSO系统的url
SSO_URL=http://localhost:8088
#购物车在cookie中保存的Key
CART_KEY=TT_CART
下面我们先来测试下拦截器是否好使,我们要启动taotao-order-web工程,但是要先将taotao-order聚合工程打包到本地maven仓库,方法是在taotao-order工程上右键----->Run As------>Maven install,如下图所示。
我们启动taotao-order-web工程,在taotao-order-web工程上右键------>Run As----->Maven build,如下图所示。
在弹出的对话框中的Goals一栏输入"clean tomcat7:run",如下图所示。然后点击"Apply",最后点击"Run"。
启动成功后,我们再点击本篇博客第一张图的"去结算",就可以看到,页面跳转到了登录页面,如下图所示。说明我们的拦截器没问题。
我们输入用户名和密码进行登录,发现登录到淘淘商城首页了,这不是我们想要去的页面,应该到订单确认页面页面才对。
登录有没有成功在login.jsp当中有判断,如下图所示,可以看到,js首先会去尝试获取从Controller端传过来的回调地址,如果取到了回调地址,那么登录成功后会跳转到回调地址,如果没有取到回调地址,那么登录成功后直接访问的便是淘淘商城首页,在上图中之所以我登录成功后访问到的是淘淘商城首页就是因为我们没有在PageController 当中添加回调地址。
下面我们到taotao-sso-web工程的PageController中简单做下修改,如下图所示,我们在showLogin方法中添加了两个参数,一个是回调地址url,另一个是用来给jsp页面添加属性的Model,回调地址url也不是凭空来的,它是由拦截器重定向时就指定好的。
修改后的PageController代码如下:
package com.taotao.sso.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 展示登录和注册页面的Controller
* @author 志鹏
*
*/
@Controller
public class PageController {
@RequestMapping("/page/register")
public String showRegister(){
return "register";
}
@RequestMapping("/page/login")
public String showLogin(String url,Model model){
model.addAttribute("redirect", url);
return "login";
}
}
既然我们修改了taotao--sso-web工程,下面我们便重启taotao-sso-web工程,重启后,我们重新来测试一下,如果你是刚登录的话,cookie中已经有你的登录信息而且token还没过期,要想达到用户未登录的情况,有两种方法,一是删除cookie中的TT_TOKEN,如下图所示,另一种方法就是等,等30分钟,30分钟后token过期。显然,我们删除cookie中的TT_TOKEN比较靠谱。
删除了cookie中的token或token过期后,我们还是从购物车列表页面(本篇博客第一张图)点击"去结算",还是会让我们登录,登录后会提示登录成功,点击"确定"后,我们看到的是下图所示页面,而不再是淘淘商城首页了。下图便是订单确认页面。
但是当前这个页面头部还是显示的是未登录状态,我们参考单点登录系统的代码,在taotao-order-web工程的js目录下找到base-v1.js,打开它修改最上面的两个方法。
修改的代码如下,这样从订单确认页面便可以点击"登录"或"注册"跳转到相应的页面。
function login() {
return location.href = "http://localhost:8088/page/login";
}
function regist() {
return location.href = "http://localhost:8088/page/register";
}
我们再修改下js目录下的taotao.js文件,其实这个文件我们只需要将端口修改为8088就可以了。
下面我们重启taotao-order-web工程,重启后,我们刷新订单确认页面,便可以看到头部有用户信息了。