CGB2005-京淘18(京淘项目Dubbo改造,用户注册 单点登录 用户名回显 用户退出 md5加密 页面定位的规则 cookie工具Api , null和空字符串的区别

注意事项

null ,, " " 的区别
String nullString = null; 连对象也没有
String isEmptyString = new String(); 有对象,对象里面没值
String blackString = " "; 有对象,对象的值为空字符串
数组字符串非空判断: if(array! = null ||array.length> 0)
int [] array 引用类型用equlas
数组为空的条件: if(array== null ||array.length== 0)
String str
字符串为空的条件: if(str == null ||str.length() ==0);
StringUtils.isEmpty(userJSON)

1 京淘项目Dubbo改造为微服务架构 只改造sso充当消费者
搭建项目结构 jar包 yml文件
2 用户模块实现 :用户注册 用户单点登录原理介绍 用户登录回显 用户退出操作
用户注册:同域请求(Dubbo微服务远程调用实现) MD5加密 定位页面的规则
Dubbo项目独有的POJO转化异常
用户单点登录原理介绍(微服务架构) —通过cookie 和redis实现 uuid随机数 凭证
查看cookie信息 cookieApi
用户登录回显(跨域):用户通过cookie信息查询用户数据. 因为用户登录成功后数据保存在了redis中,所以实际上是通过ticket获取redis中的业务数据(k-v结构,根据k获取v)
封装cookie的工具Api,之后重构单点登录和用户登录回显
用户退出操作:重定向到首页

1 京淘项目Dubbo改造

1.1 改造计划(先搭建项目结构)

1.jt-common 充当接口项目
2.jt-sso 充当提供者
3.jt-manage 充当提供者
4.jt-web 充当用户的消费者

1.2 添加jar包文件(jt)

在这里插入图片描述

 <dependency>
      <groupId>com.alibaba.boot</groupId>
      <artifactId>dubbo-spring-boot-starter</artifactId>
      <version>0.2.0</version>
  </dependency>

1.3 创建DubboUserService接口(common)

说明:在jt-common是同样的项目,已经被别的项目依赖了,所以只需要添加Dubbo的业务接口即可。
在这里插入图片描述

1.4 改造JT-SSO为生产者(sso)

1.4.1 编辑生产者实现类–DubboUserServiceImpl

说明:在SSO项目添加实现类,原来的类不需要改变只需要添加新的实现类,mapper接口可以用以前的。
在这里插入图片描述

1.4.2 编辑生产者YML配置文件

在这里插入图片描述

server:
  port: 8093
  servlet:
    context-path: /
spring:
  datasource:
    #引入druid数据源
    #type: com.alibaba.druid.pool.DruidDataSource
    #driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: root

#关于Dubbo配置
dubbo:
  scan:
    basePackages: com.jt    #指定dubbo的包路径 为了扫描此项目下的dubbo注解(@service),可以指定大点的范围com,jt
  application:              #应用名称
    name: provider-user     #一个接口对应一个服务名称(一个接口可以有多个实现,但是如果实现同一个接口则提供的服务也应该是同一个。 eg:老王 老李都卖菜则实现同一个接口,老孙卖肉则和老王老李实现不同的借口)
  registry: #注册中心 2181连接的是从机 backup(备用) 用户获取数据从机中获取 主机只负责监控整个集群 实现数据同步,所以这个地方要连接从机而不是主机
    address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183
  protocol:  #指定协议 name:dubbo固定写法
    name: dubbo  #使用dubbo协议(tcp-ip)  web-controller直接调用sso-Service
    port: 20880  #每一个服务都有自己特定的端口 不能重复.


  mvc:
    view:
      prefix: /WEB-INF/views/
      suffix: .jsp
#mybatis-plush配置
mybatis-plus:
  type-aliases-package: com.jt.pojo
  mapper-locations: classpath:/mybatis/mappers/*.xml
  configuration:
    map-underscore-to-camel-case: true

logging:
  level: 
    com.jt.mapper: debug

1.4.3 启动生产者SSO测试

说明:测试Dubbo服务是否启动成功
在这里插入图片描述

1.5 改造JT-Web为消费者(web)

1.5.1 编辑消费者UserController

说明:注入DubboService对象
在这里插入图片描述

1.5.2 编辑消费者YML配置文件

server:
  port: 8092    
spring:     #定义springmvc视图解析器
  mvc:
    view:
      prefix: /WEB-INF/views/
      suffix: .jsp
dubbo:
  scan:
    basePackages: com.jt
  application:
    name: consumer-web   #定义消费者名称
  registry:               #注册中心地址
    address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183 

在这里插入图片描述

1.5.3 启动消费者Web测试

在这里插入图片描述

2 用户模块实现

2.1 用户注册

2.1.1 URL分析

根据url地址说明请求为同域请求.
在这里插入图片描述

参数信息:
在这里插入图片描述

2.1.2 页面JS分析

说明:
1.根据页面url地址 查找页面JS的位置 ctrl+h
2.复制的规则:复制url在写死不变的地址 一般发送的请求中路径是固定的,域名有可能写有可能不写,所以保险起见复制路径即可。
在这里插入图片描述
具体页面js:
说明:根据分析获取返回值数据信息应该为SysResult对象.
在这里插入图片描述

2.1.3 编辑UserController(web项目)

在这里插入图片描述

package com.jt.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.jt.pojo.User;
import com.jt.service.DubboUserService;
import com.jt.vo.SysResult;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller //由于设计到页面跳转功能.
@RequestMapping("/user")
public class UserController {
    @Reference(timeout = 3000) //这个注解也可以设置连接超时时间
    private DubboUserService dubboUserService;


    /**
     * 完成用户注册操作.
     * url地址: http://www.jt.com/user/doRegister
     * 参数:    {password:_password,username:_username,phone:_phone}
     * 返回值:  SysResult对象   返回的是JSON串
     * 业务说明:通过dubbo框架将user信息RPC传入jt-sso实现数据的入库操作.
     * */
    @RequestMapping("/doRegister")
    @ResponseBody
    public SysResult saveUser(User user){
        //消费者给予dubbo协议将user对象进行远程网络数据传输.需要序列化,这里的use虽然没呀序列化但是他继承的BasePojo
        dubboUserService.saveUser(user);
        return SysResult.success();  //这样写就不需要返回值了
    }

}

2.1.4 编辑UserService(sso)

说明:中立接口在common中.
在这里插入图片描述

package com.jt.service;

import com.alibaba.dubbo.config.annotation.Service;
import com.jt.mapper.UserMapper;
import com.jt.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

@Service(timeout =3000)
public class DubboUserServiceImpl implements DubboUserService{
    @Autowired
    private UserMapper userMapper;

    /**
     * 1.用户没有传递邮箱地址,邮箱暂时使用电话号码代替
     * 2.需要将密码进行加密处理    md5/md5-hash
     * 3.入库操作注意事务控制
     * @param user
     * @return
     */
    @Override
    @Transactional  //  控制一下事物 失败了就不执行  (加在接口也行,代表所有的实现类都要事务控制)
    public void saveUser(User user) {
        user.setEmail(user.getPhone());//暂时用电话代替邮箱
        //1.获取明文
        String password = user.getPassword();
        //2.利用Spring的工具API进行加密操作  md5-hash的操作     里面要求是字节  想用盐值可以自己加上
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        user.setPassword(password);
        userMapper.insert(user);
    }
}

2.1.5 启动测试(生产者 消费者)

在这里插入图片描述

2.1.6 关于POJO转化异常说明

这个异常时Dubbo项目独有的.

报错说明: 由于SpringBoot配置了热部署的工具,当代码进行修改之后,程序就会重新启动. 在重启的过程中程序又会再次链接zookeeper注册中心.由于zk的心跳检测机制存在超时时间,可能在zk中会出现2条一模一样的服务的提供者的信息. ( 又一次校验的时候宕机了,在校验第二次时由于重启的数独较快,所以没有认为你宕机了.但确实有链接了一次ZK,所以在注册中心有2个一摸一样的信息,所以在调用某个服务时,注册中心会出现2个一摸一样的信息,用户连接的时候不知道选择哪个.就会报错)
解决方案: 需要手动的重启服务器即可.(先启提供者,再起消费者)

在这里插入图片描述

2.2 用户单点登录原理介绍

2.2.1 需求说明

说明: 如果采用SESSION的方式实现用户的登录操作,由于nginx负载均衡的策略,用户可以访问不同的服务器.但是Session不能共享,所以导致用户频繁的登录. 用户的体验不好.

解释:session不共享,将数据信息保存在服务端的session中一旦关闭服务器session就会消失,用户就需要重新登陆。一台服务器,常规的实现免密登录执行流程一般是:在后台服务器session中保存用户会话信息,之后生成一个凭证(eg:去酒店的贵宾卡)返回给浏览器客户端保存在cookie中,即使后台服务器关闭(去酒店只要拿出卡 就能放行不认人)用户也可以通过cookie的凭证实现登陆操作。

问题:如果后台不止一台服务器,由于nginx的负载均衡策略(nginx可以做简单地负载均衡,负载几个前端服务器影响不大)需要访问不同的服务器,而session由于不共享(即不同的前端服务器保存的session不同),所以访问另一台没有凭证的服务器时需要重新登陆。
在这里插入图片描述

如何解决上述访问多台服务器也不需要重新登录???
答:不能保存在session中,应该保存在一个第三方公共的地方,并且这个第三方后台服务器都能够进行访问redis实现

在这里插入图片描述

2.2.2 SSO(单点登录)介绍

单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的 一种登录方式

eg:登录微信后可以使用 公众号 朋友圈等功能。

2.2.3 京淘项目单点登录设计(借助redis)

在这里插入图片描述
步骤:
1.当用户输入用户名和密码等用户信息点击注册时,将请求发送给JT-WEB消费者服务器。
2.JT-WEB服务器需要将用户信息传递给JT-SSO单点登录系统完成数据的校验。
3.如果在单点登录系统中效验失败,则直接返回错误信息即可。
4.如果在单点登录系统中效验成功,则将用户查询到的数据经过处理之后保存到redis中,并且生动态的成TICKET凭证,(注意超时时间)。
5.之后JT-SSO将登录的凭证传给JT-WEB服务器。
6.JT-WEB服务器将获取的TICKET凭证信息保存到客户端的Cookie中,方便下次登录(注意超时时间)。

总结:
第一轮登录,用户在前端服务器输入账号 密码等数据信息,因为前端服务器不能连接数据库所以把数据发送到后台服务器进行验证,如果查询到有值则把用户查询的信息转化为json保存到redis中,并且动态生成凭证返回到cookie中,此时登陆成功。如果没有查询到则直接返回错误信息。
第二轮登录,先根据cookie的凭证到中央的redis校验是否有ticket凭证,如果有则实现免密登录,没有则跳转到登录页面重新输入用户信息进行登录。

为什么同UUID 而不用MD5加密,因为md5有可能会重复. 而UUID理论上可以重复,但实际上概率非常非常低.

2.3 用户单点登录具体实现

此模块只考虑第一轮登录

2.3.1 用户登录页面url分析

说明: r=0.8989367429030823(随机数) 用来防止浏览器缓存的.
在这里插入图片描述

2.3.2 参数分析

在这里插入图片描述

2.3.3 页面JS分析

同理:ctrl+h /user/doLogin
在这里插入图片描述

  $.ajax({
            type: "POST",
            url: "/user/doLogin?r=" + Math.random(),
            contentType: "application/x-www-form-urlencoded; charset=utf-8",
            data: {username:_username,password:_password},
            dataType : "json",
            error: function () {
                $("#nloginpwd").attr({ "class": "text highlight2" });
                $("#loginpwd_error").html("网络超时,请稍后再试").show().attr({ "class": "error" });
                $("#loginsubmit").removeAttr("disabled");
                $this.removeAttr("disabled");
            },
            success: function (result) {
                //如果数据不为null时执行
                if (result) {
                    var obj = eval(result);
                    if (obj.status == 200) {
                    	obj.success = "http://www.jt.com";
                    	.....

2.3.4 编辑UserController(web)

在浏览器查看整个域名下的cookie:以京东为例。也可以在Network查看单个的请求cookie

在这里插入图片描述
在这里插入图片描述

package com.jt.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.jt.pojo.User;
import com.jt.service.DubboUserService;
import com.jt.vo.SysResult;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

@Controller //由于设计到页面跳转功能.
@RequestMapping("/user")
public class UserController {
    @Reference(timeout = 3000) //这个注解也可以设置连接超时时间
    private DubboUserService dubboUserService;

    /**
     * 完成用户的登录操作
     * 1. url地址:http://www.jt.com/user/doLogin?r=0.8989367429030823
     * 2. 参数:   username/password
     * 3. 返回值: SysResult对象
     * 4.cookie:
     *  4.1 setPath("/") path表示如果需要获取cookie中的数据,则url地址所在的路径设定。
     *      url:http://www.jt.com/user/findAll
     *      cookie.setPath("/"); 代表可以拿到/下cookie的所有信息,一般都是 / 代表根目录
     *      cookie.setPath("/persion"); 代表url的路径为/persion才可以拿到cookie,现在是url是:/user所以不能拿到cookie
     *  4.2 setDomain("jt.com") 设置cookie共享范围 即只要域名的后缀以 jt.com结尾则cookie共享
     *  4.3 setMaxAge(xxx)  设置cookie存活时间 单位 秒
     *      cookie.setMaxAge(-1);  关闭浏览器会话时删除
     *      cookie.setMaxAge(0);   立即删除cookie
     *      cookie.setMaxAge(100); cookie可以存储的时间单位是秒
     */
    @RequestMapping("/doLogin")
    @ResponseBody  //将返回的结果变为JSON格式
    public SysResult doLogin(User user, HttpServletResponse response){

        //1.获取凭证ticket 如果校验成功则凭证有值 如果校验失败则凭证为空
        String ticket = dubboUserService.doLogin(user);
        //2.校验ticket是否有值.为空返回fail()
        if(StringUtils.isEmpty(ticket)){
            //用户名或者密码错误
            return SysResult.fail();
        }else{
            //3.如果用户的ticket不为null,则表示登录正确,需要将ticket保存到cookie中
            //Cookie要求   1.7天有效  2.要求cookie可以在jt.com的域名中共享  3.cookie权限
            //创建cookie对象 cookie的名(注意和js里面保持一致) cookie要保存值
            Cookie cookie = new Cookie("JT_TICKET",ticket);
            
            cookie.setMaxAge(7*24*3600); // 设置cookie存活时间  单位秒
            cookie.setPath("/");//定于cookie的有效范围
            cookie.setDomain("jt.com"); //在jt.com中实现页面域名共享.是实现单点登录必备要素(我们整个jt项目的域名都已jt.com结尾)

            response.addCookie(cookie); //利用response将cookie保存到浏览器客户端中.
            return SysResult.success();
        }
    }   

在这里插入图片描述

2.3.5 编辑UserServiceImpl(sso)

说明:微服务架构接口在common中
在这里插入图片描述

package com.jt.service;

import com.alibaba.dubbo.config.annotation.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jt.mapper.UserMapper;
import com.jt.pojo.User;
import com.jt.util.ObjectMapperUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import redis.clients.jedis.JedisCluster;

import java.util.UUID;

@Service(timeout =3000)
public class DubboUserServiceImpl implements DubboUserService{
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private JedisCluster jedisCluster;


    /**
     * 1.根据用户名和密码查询数据库
     * 2.校验用户数据的有效性.
     * 3.如果用户的数据是正确的 则开始进行单点登录操作.
     * 4.如果用户数据不正确 则ticket数据为null即可.
     * @param user
     * @return
     */
    @Override
    public String doLogin(User user) {

        //1.将密码进行加密处理(因为注册时密码加密了,相同的数据使用相同的MD5加密方式得到的结果相同)
        String password = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
        user.setPassword(password);
        //常规写法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",user.getUsername())
                    .eq("password",user.getPassword());
        User userDB = userMapper.selectOne(queryWrapper);

        /**
         * 优化写法: 如果传递的是对象,则根据对象中不为null的属性充当where条件
         *  QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
         *  User userDB = userMapper.selectOne(queryWrapper);
         */


        /**
         * 2.校验数据是否有效,思路:
         * 如果查询的对象userDB为null,校验失败
         * 如果查询的对象userDB不为null,校验成功,之后保存到redis,生成ticket返回。
         */
        if(userDB == null){
            //2.1 输入的用户名或密码错误
            return null;
        }else{
            //2.2 userDB数据不为null,用户的输入信息正确.开启单点登录操作.
            /**
             * 2.2.1 脱敏处理:
             * 如果直接把查询的结果userDB转化为json保存到redis中,密码暴露给别人了不安全.
             * 解决:设置一个假密码保存在redis中,因为加密码没存入数据库所以不会影响数据库信息。
             */
            userDB.setPassword("123456你信不??");
            String userJSON = ObjectMapperUtil.toJSON(userDB);//把userDB对象通过封装好的工具api转化为json数据(用户名 假密码)
            //通过uuid动态生成ticket凭证    替换横线(-)   注意是""而不是null,因为uuid生成后是通过-连接的随机字符串,当然这里也可以不用替换
            String ticket = UUID.randomUUID().toString().replace("-", "");
            // 通过配置类注入redis集群对象 将数据(凭证 超时时间 查询结果)保存到redis中    注意设定超时时间
            jedisCluster.setex(ticket, 7*24*60*60, userJSON);
            return ticket; //返回凭证
        }
    }
}

2.3.6 启动生产者 消费者测试(zk reids集群启动)

说明:cookie的value为uuid生成的凭证,因为用空字符串代替了-,所以显示连续的字符串。
若没有代替则显示的格式为:2da77ab6-f3a9-4d37-9d02-5f3e159b42c7
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.4 用户登录回显

2.4.1 用户信息回显业务说明

说明:用户登陆成功后会自动发送请求,在www.jt.com页面回显用户的名称
在这里插入图片描述

2.4.2 页面url分析

步骤:
1). 首先在登录页面登陆成功后跳转到www.jt.com页面
在这里插入图片描述

2).F12分析页面url
说明:点击刷新按钮,找到报红的请求,根据url分析此为 jsonp的跨域方式,路径拼接的有cookie的凭证。
在这里插入图片描述

2.4.3 页面JS分析

说明:
1.ctrl+h+固定路径+enter跳转到页面请求。
2.用户登录完成后会自动发送请求进行跨域访问。
在这里插入图片描述

var TT = JT = {
	checkLogin : function(){
		var _ticket = $.cookie("JT_TICKET");
		if(!_ticket){  //如果ticket为空
			return ;   //程序结束
		}
		//当dataType类型为jsonp时,jQuery就会自动在请求链接上增加一个callback的参数
		$.ajax({
			url : "http://sso.jt.com/user/query/" + _ticket,
			dataType : "jsonp",
			type : "GET",
			success : function(data){
				if(data.status == 200){
					//把json串转化为js对象
					var _data = JSON.parse(data.data);
					var html =_data.username+",欢迎来到京淘!<a href=\"http://www.jt.com/user/logout.html\" class=\"link-logout\">[退出]</a>";
					$("#loginbar").html(html);
				}
			}
		});
	}
}

$(function(){
	// 查看是否已经登录,如果已经登录查询登录信息
	TT.checkLogin();
});

2.4.4 编辑JT-SSO UserController

说明:跨域是web页面的js发送请求到sso服务器,说会控制层在sso里面写。
在这里插入图片描述

package com.jt.controller;

import com.fasterxml.jackson.databind.util.JSONPObject;
import com.jt.pojo.User;
import com.jt.service.UserService;
import com.jt.vo.SysResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.JedisCluster;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@RestController
@RequestMapping("/user")//业务名称最好写类上
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private JedisCluster jedisCluster;

    /**
     * 业务实现:
     *  1.用户通过cookie信息查询用户数据. 因为用户登录成功后数据保存在了redis中,所以实际上是通过ticket获取redis中的业务数据(k-v结构,根据k获取v)
     *  2.url请求: http://sso.jt.com/user/query/+ _ticket
     *  3.参数:    参数在url中. 利用restFul获取
     *  4.返回值要求: SysResult对象(user对象的JSON形式,在redis中存的就是json形式)
     *   跨域不需要拦截域名
     */
    @RequestMapping("/query/{ticket}") //resful风格
    public JSONPObject findUserByTicket(@PathVariable String ticket, HttpServletResponse response, String callback){
        //1.redis中的lru算法清空数据 2.用户可能在浏览器删除或修改cookie信息 所以需要进行if判断是否存在ticket
        String userJSON = jedisCluster.get(ticket);//从redis中获取ticket
        if(StringUtils.isEmpty(userJSON)){
            //如果根据ticket查询有误,则应该删除Cookie信息(cookie不能存一个假凭证需要删除 eg:去酒店给一个假的会员卡开房).
            Cookie cookie = new Cookie("JT_TICKET","");//value随便写个字符串都行 但不能写null浏览器解析会有问题
            //cookie删除的特点: 要保证和原来的cookie一摸一样,所以把这些属性也要加上后再删除.
            cookie.setDomain("jt.com");
            cookie.setPath("/");
            cookie.setMaxAge(0); //通过设定超时时间删除cookie  API中没有提供直接删除Cookie的方法
            /**
             * 由于浏览器是根据cookie的名字来区分cookie,如果前后两次向浏览器发送同名的cookie,
             * 后发送的cookie会覆盖之前发送的cookie。而后发送的cookie设置了生存时间为零,因此浏览器收到后也会立即删除!
             */
            response.addCookie(cookie);
            return new JSONPObject(callback, SysResult.fail());//这是按照jsonp的形式返回.
        }else{
            //返回正确的有效数据
            return new JSONPObject(callback, SysResult.success(userJSON));
        }

    }
}    

2.4.5 启动测试(sso web)

在这里插入图片描述
如果用户删cookie信息则需要重新登陆,cookie没有了就不走页面的跳转js了。
在这里插入图片描述

2.5 编辑工具Api优化Cookie(common)

说明: 每次获取cookie信息,都会重新写获取cookie的代码太麻烦,所以把它封装成为一个API放在在common中.

package com.jt.util;

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

public class CookieUtil {

    /**
     * 该工具API主要的任务
     *      1.根据cookie的名称 返回valve的值
     *      3.新增cookie方法
     *      4.删除cookie方法
     */

    //1.根据cookie的名称 返回valve的值
    //参数:cookie的名 request对象(存cookie通过response对象,取cookie通过request对象)
    public static String getCookieValue(String cookieName, HttpServletRequest request){
        //通过request获取请求中的所有cookie组成的数组,没有获取到返回null
        Cookie[] cookies = request.getCookies();
        //用户可能操作浏览器删除cookie,所以需要校验请求中是否有cookie
        /**注意 null "" 空 的区别:
         * eg:桌子上有个杯子,杯子有水。
         *   null,代表连杯子都没有(连对象也没有)
         *   ""空字符串,表示有杯子且杯子有东西,但是这个东西恰好是空字符串(有对象,对象的值为空字符串. 空串长度为1)
         *   空:  代表杯子里面啥也没有(有对象,对象的值为空.   空值长度为 o)
         * 所以数组判断不为空(有值),需要先判断有对象且对象里面有值。
         */
        if(cookies !=null && cookies.length >0) {
            for (Cookie cookie : cookies) { //因为cookie没有提供直接返回具体的cookie名的方法所以需要遍历
                if (cookieName.equals(cookie.getName())) {
                    return cookie.getValue();//根据key获取cookie中的value
                }
            }
        }
        return null ; //没有值返回空
    }

    //2.新增 不需要返回值
    //参数:cookie名字 值 存活时间 有效范围 域名共享  redponse对象
    public static void addCookie(String cookieName, String cookieValue,int maxAge,String path,String domain, HttpServletResponse response){
        Cookie cookie = new Cookie(cookieName,cookieValue);
        cookie.setMaxAge(maxAge);
        cookie.setPath(path);
        cookie.setDomain(domain);
        response.addCookie(cookie);
    }
    //3.删除
    /**
     * 参数:cookie名字 值 存活时间 有效范围 域名共享  redponse对象
     * 代码结构和新增几乎和新增相同,只不过2个参数有变化:
     *    cookie的value:随便传一个字符串/空字符串也可以 但不能为null
     *    setMaxAge超时时间:设置为 0
     * 所以可以直接调用新增的addCooKie方法,实现代码复用。
     * 方法实现复用.
     */
    public static void deleteCookie(String cookieName, String cookieValue,int maxAge,String path,String domain, HttpServletResponse response){
        //复用addCookie新增代码
        addCookie(cookieName,cookieValue,maxAge,path,domain,response);
    }

}

2.5.1 重构单点登录和用户登录回显(3.3、3.4)

单点登录:
在这里插入图片描述
用户登录回显:
在这里插入图片描述

2.5.2 再次测试(启动 sso web)

用户登陆成功后回显用户信息
在这里插入图片描述

2.6 用户退出操作

2.6.1 业务说明

如果用户点击退出操作,首先应该删除Redis中的数据,其次删除Cookie中的数据,之后重定向到系统首页。

2.6.2 页面url分析

步骤:
在这里插入图片描述
域名相同:同域请求,因为微服务架构的controller在web项目中,所以控制层的代码需要写在web项目中。
在这里插入图片描述

2.6.3 页面js分析

说明:
1.点击退出直接跳转到系统首页,由页面js分析可知请求是由< a>标签发送的普通方式的请求,普通方式的请求是同步请求。(ajax可以指定同步 异步 普通方式只能是同步请求)。
2.普通方式发送的同步请求重新跳转到系统首页-----重定向(redirect)。ajax方式跳转----在页面js中调用方法跳转。

在这里插入图片描述

2.6.4 编辑UserController(web)

重定向说明:
在这里插入图片描述

package com.jt.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.jt.pojo.User;
import com.jt.service.DubboUserService;
import com.jt.util.CookieUtil;
import com.jt.vo.SysResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import redis.clients.jedis.JedisCluster;

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

@Controller 
@RequestMapping("/user")
public class UserController {
    @Reference(timeout = 3000) //这个注解也可以设置连接超时时间
    private DubboUserService dubboUserService;
    @Autowired //进行rpc远程调用才用dubbo的注解,用自己家的就用普通的注解
    private JedisCluster jedisCluster;
    /**
     * 实现用户的登出操作,重定向到系统首页:
     * url: http://www.jt.com/user/logout.html
     * 参数: 暂时没有
     * 返回值: 重定向到系统首页(所以返回值类型为 string)
     * 业务流程:
     *      1.删除Redis中的数据 (通过k删除v,k是凭证保存在cookie中,根据cookie中的凭证到redis中删除)
     *      2.删除cookie记录 (已经封装好api了)
     */
    @RequestMapping("/logout")
    public String logout(HttpServletRequest request, HttpServletResponse response){
        //1.根据cookie指定的名称(JT_TICKET)获取cookie里面存的值---凭证ticket(工具api)
        String ticket = CookieUtil.getCookieValue("JT_TICKET", request);
        //2.判断ticket是否为null (用户操作浏览器可能导致cookie删除,返回值为null,所以需要判断)
        /**
         * 流程:ticket为空直接重定向首页数据,ticket不为空删除cookie redis后再重定向首页页面
         * 常规为空判断:如果ticket为null重定向页面,如果不为空先删除在重定向首页页面
         * 非空判断:如果ticket不为空 把重定向的页面代码放到if外面可以省略一行代码。
         */
        if(!StringUtils.isEmpty(ticket)){
            //3.删除redis redis的Api提供的方法.
            jedisCluster.del(ticket);
            //4.删除cookie
            CookieUtil.deleteCookie("JT_TICKET","",0,"/","jt.com",response);
        }
        return "redirect:/";// www.jt.com/ 地址后面省略了"/",所以重定向到系统根目录下的页面,就是首页
    }

        /*常规为空判断
        if(StringUtils.isEmpty(ticket)){
            return "redirect:/";
        }else{
            jedisCluster.del(ticket);
            CookieUtil.deleteCookie("JT_TICKET","",0,"/","jt.com",response);
            return "redirect:/";

        }*/
 }      

在这里插入图片描述

2.6.5 访问测试(sso web)

点击退出,重定向到系统首页
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值