尚品汇-登录、退出实现(三十四)

目录:

   流程分析

(1)实现思路

(2)搭建认证中心模块service-user

(3)配置网关server-gateway

(4)在web-all模块添加实现

流程分析

公私钥解决:

认证中心有个私钥认证完之后,对user进行加密生成tocken,tocken里面有用户信息,再次访问购物车,购物车的公钥正好能够解析用户信息,这个就不需要购物车再次通过tocken去认证服务器询问是够用户的tocken是否正确,避免多次访问请求认证服中心,只需要登录一次就够了

(1)实现思路

  1. 用接收的用户名密码核对后台数据库
  2. 核对通过,用uuid生成token
  3. 将用户id加载到写入redis,redis的key为token,value为用户id。
  4. 登录成功返回token与用户信息,将token与用户信息记录到cookie里面
  5. 重定向用户到之前的来源地址。

数据库表:user_info,并添加一条数据!密码应该是加密的!

user_address

user_info

 

(2)搭建认证中心模块service-user

搭建方式如service-item 

pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
      <groupId>com.atguigu.gmall</groupId>
      <artifactId>service</artifactId>
      <version>1.0</version>
   </parent>

   <version>1.0</version>
   <artifactId>service-user</artifactId>
   <packaging>jar</packaging>
   <name>service-user</name>
   <description>service-user</description>

   <build>
      <finalName>service-user</finalName>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>

</project>

添加配置文件

bootstrap.properties

spring.application.name=service-user
spring.profiles.active=dev
spring.cloud.nacos.discovery.server-addr=192.168.254.129:8848
spring.cloud.nacos.config.server-addr=192.168.254.129:8848
spring.cloud.nacos.config.prefix=${spring.application.name}
spring.cloud.nacos.config.file-extension=yaml
spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml

启动类

package com.atguigu.gmall.user;


@SpringBootApplication
@ComponentScan({"com.atguigu.gmall"})
@EnableDiscoveryClient
public class ServiceUserApplication {

   public static void main(String[] args) {
      SpringApplication.run(ServiceUserApplication.class, args);
   }

}

封装登录接口

 编写接口

package com.atguigu.gmall.user.service;


public interface UserService {

    /**
     * 登录方法
     * @param userInfo
     * @return
     */
    UserInfo login(UserInfo userInfo);

}

Mapper:UserInfoMapper


package com.atguigu.gmall.user.mapper;

import com.atguigu.gmall.model.user.UserInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}

实现类

package com.atguigu.gmall.user.service.impl;


@Service
public class UserServiceImpl implements UserService {

    // 调用mapper 层
    @Autowired
    private UserInfoMapper userInfoMapper;


    @Override
    public UserInfo login(UserInfo userInfo) {
        // select * from userInfo where userName = ? and passwd = ?
        // 注意密码是加密:
        String passwd = userInfo.getPasswd(); //123
        // 将passwd 进行加密
        String newPasswd = DigestUtils.md5DigestAsHex(passwd.getBytes());

        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("login_name", userInfo.getLoginName());
        queryWrapper.eq("passwd", newPasswd);
        UserInfo info = userInfoMapper.selectOne(queryWrapper);
        if (info != null) {
     
            return info;
        }
        return null;
    }
}

IpUtil : 

package com.atguigu.gmall.common.util;

import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 获取ip地址
 */
public class IpUtil {

    public static String getIpAddress(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress="";
        }
        // ipAddress = this.getRequest().getRemoteAddr();

        return ipAddress;
    }
    // 网关中获取Ip地址
    public static String getGatwayIpAddress(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ip = headers.getFirst("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getAddress().getHostAddress();
        }
        return ip;
    }
}

控制器:PassportApiController 

package com.atguigu.gmall.user.controller;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.util.IpUtil;


/**
 * <p>
 * 用户认证接口
 * </p>
 */
@RestController
@RequestMapping("/api/user/passport")
public class PassportApiController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 登录
     * @param userInfo
     * @param request
     * @param response
     * @return
     */
    @PostMapping("login")
    public Result login(@RequestBody UserInfo userInfo, HttpServletRequest request, HttpServletResponse response) {
        System.out.println("进入控制器!");
        UserInfo info = userService.login(userInfo);

        if (info != null) {
            String token = UUID.randomUUID().toString().replaceAll("-", "");
            HashMap<String, Object> map = new HashMap<>();
            map.put("nickName", info.getNickName());
            map.put("token", token);

            JSONObject userJson = new JSONObject();
            userJson.put("userId", info.getId().toString());
            userJson.put("ip", IpUtil.getIpAddress(request));
            redisTemplate.opsForValue().set(RedisConst.USER_LOGIN_KEY_PREFIX + token, userJson.toJSONString(), RedisConst.USERKEY_TIMEOUT, TimeUnit.SECONDS);
            return Result.ok(map);
        } else {
            return Result.fail().message("用户名或密码错误");
        }
    }

    /**
     * 退出登录
     * @param request
     * @return
     */
    @GetMapping("logout")
    public Result logout(HttpServletRequest request){
        redisTemplate.delete(RedisConst.USER_LOGIN_KEY_PREFIX + request.getHeader("token"));
        return Result.ok();
    }
}

 

(3)配置网关server-gateway

- id: service-user
  uri: lb://service-user
  predicates:
  - Path=/*/user/**

- id: web-passport
  uri: lb://web-all
  predicates:
  - Host=passport.gmall.com

(4)在web-all模块添加实现 

在web-all 项目中跳转页面

package com.atguigu.gmall.all.controller;

/**
 * <p>
 * 用户认证接口
 * </p>
 *
 */





@Controller
public class PassportController {

    /**
     *
     * @return
     */
    @GetMapping("login.html")
    public String login(HttpServletRequest request) {
        String originUrl = request.getParameter("originUrl");
        request.setAttribute("originUrl",originUrl);
        return "login";
    }

}

登录页面

页面资源: \templates\login1.html

login1.html 没有公共头部信息

login.html 有公共头部信息

Html:Login代码

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE">
	<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
	<title>尚品汇,欢迎登录</title>
	<link rel="icon" href="./img/favicon.ico">

    <link rel="stylesheet" type="text/css" href="/css/all.css" />
    <link rel="stylesheet" type="text/css" href="/css/pages-login.css" />
	<div th:include="common/head :: head"></div>
</head>

<body>
	<!-- 头部栏位 -->
	<!--页面顶部-->
	<div th:include="common/loginHeader :: loginHeader"></div>

	<div class="login-box">

		<!--loginArea-->
		<div class="loginArea">
			<div class="py-container login">
				<div class="loginform">
					<ul class="sui-nav nav-tabs tab-wraped">
						<li id="tabOtherLogin" onclick="switchOtherLogin()">
							<a href="javascript:void(0)" data-toggle="tab" >
								<h3>扫描登录</h3>
							</a>
						</li>
						<li id="tabSuiForm" class="active" onclick="switchSuiForm()">
							<a href="javascript:void(0)" data-toggle="tab" >
								<h3>账户登录</h3>
							</a>
						</li>
					</ul>
					<div class="tab-content tab-wraped">
						<div id="index" class="tab-pane ">
							<p>二维码登录,暂为官网二维码</p>
							<img src="./img/wx_cz.jpg" />
						</div>
						<div id="profile" class="tab-pane active">
							<form class="sui-form">
								<div class="input-prepend"><span class="add-on loginname"></span>
									<input id="inputName" type="text" v-model="user.loginName" placeholder="邮箱/用户名/手机号" class="span2 input-xfat">
								</div>
								<div class="input-prepend"><span class="add-on loginpwd"></span>
									<input id="inputPassword" type="password" v-model="user.passwd" placeholder="请输入密码" class="span2 input-xfat">
								</div>
								<div class="setting">
									<label class="checkbox inline">
										<input name="m1" type="checkbox" value="2" checked="">
										自动登录
									</label>
									<span class="forget">忘记密码?</span>
								</div>
								<div class="logined">
									<a class="sui-btn btn-block btn-xlarge btn-danger" href="javascript:" @click="submitLogin()">登&nbsp;&nbsp;录</a>
								</div>
							</form>
							<div class="otherlogin">
								<div class="types">
									<ul>
										<li><img src="./img/qq.png" width="35px" height="35px" /></li>
										<li><img src="./img/sina.png" /></li>
										<li><img src="./img/ali.png" /></li>
										<li><img src="./img/weixin.png" /></li>
									</ul>
								</div>
								<span class="register"><a href="register.html" target="_blank">立即注册</a></span>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
		<!--foot-->
		<div class="py-container copyright">
			<ul>
				<li>关于我们</li>
				<li>联系我们</li>
				<li>联系客服</li>
				<li>商家入驻</li>
				<li>营销中心</li>
				<li>手机尚品汇</li>
				<li>销售联盟</li>
				<li>尚品汇社区</li>
			</ul>
			<div class="address">地址:北京市昌平区宏福科技园综合楼6层</div>
			<div class="beian">京ICP备19006430号
			</div>
		</div>
	</div>

<!--页面底部END-->
<script src="/js/api/login.js"></script>
<script th:inline="javascript">
	var item = new Vue({
		el: '#profile',

		data: {
            originUrl: [[${originUrl}]],// 后台存储这么一个originUrl
            user: {
                loginName: '',
                passwd: ''
            }
		},

		created() {
		},

		methods: {
            submitLogin() {
            	/*后台就可以使用UserInfo 对象接收*/
                login.login(this.user).then(response => {
                    //	debugger
                    if (response.data.code == 200) {
                        //  把token存在cookie中、也可以放在localStorage中
						//	result.ok(map);
						//	response 相当于Result
						//	response.data 相当于 Result.ok();
						//	response.data.data  data看做一个map 相当于 Result.ok(data);
						//	response.data.map.token 相当于 result.ok(map)
						//	map 中有token 同时还有 nickName
                        auth.setToken(response.data.data.token)
						//	response.data.data 相当于返回来的map
						//	JSON.stringify(response.data.data) 将map 数据变为json对象,存储到cookie 中
                        auth.setUserInfo(JSON.stringify(response.data.data))

						//	输入日志:是否有 originUrl 回跳url!
						//	originUrl = 记录用户在哪点击的登录url ,当用户登录成功之后,又跳转到了原来的url!
                        console.log("originUrl:"+this.originUrl);
                        if(this.originUrl == ''){
                            window.location.href="http://www.gmall.com/index.html"
                            return ;
                        } else {
                            window.location.href = decodeURIComponent(this.originUrl)
						}
                    } else {
						alert(response.data.data.message)
					}

                })
            }
        }
	})
</script>
</body>
</html>

 提取头部信息

提取头部信息我们会用到thymeleaf 两个标签:

th:fragment:定义代码块
th:include:将代码块片段包含的内容插入到使用了th:includeHTML标签中
1,定义头部代码块(/common/header.html),关键代码
<div id="nav-bottom" th:fragment="header">
    <!--顶部-->
	<div class="nav-top" id="header">
		...
	</div>
</div>
2,在其他页面引用头部代码块
<div th:include="common/header :: header"></div>

 头部登录状态处理

思路:登录成功后我们将用户信息写入了cookie,所以我们判断cookie中是否有用户信息,如果有则显示登录用户信息和退出按钮,我们采取vue的渲染方式

关键代码
Header.html 中
<ul class="fl">
    <li class="f-item">尚品汇欢迎您!</li>
    <li  v-if="userInfo.nickName == ''" class="f-item">请<span><a href="javascript:" @click="login()">登录</a></span> <span><a href="#">免费注册</a></span></li>
    <li  v-if="userInfo.nickName != ''" class="f-item"><span>{{userInfo.nickName}}</span> <span><a href="javascript:" @click="logout()">退出</a></span></li>
</ul>

<script type="text/javascript" src="/js/plugins/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/js/plugins/jquery.cookie.js"></script>
<script src="/js/plugins/vue.js"></script>
<script src="/js/plugins/axios.js"></script>
<script src="/js/auth.js"></script>
<script src="/js/request.js"></script>
<script src="/js/api/login.js"></script>
<script th:inline="javascript">
    var item = new Vue({
        el: '#header',

        data: {
            userInfo: {
                nickName: '',
                name: ''
            }
        },

        created() {
            this.showInfo()
        },

        methods: {
            showInfo() {
                // debugger
                if(auth.getUserInfo()) {
                    this.userInfo = auth.getUserInfo()
                    console.log("--------"+this.userInfo.nickName)
                }
            },

            
            logout() {
                //debugger
                login.logout().then(response => {
                    console.log("已退出")
                    auth.removeToken()
                    auth.removeUserInfo()

                    //跳转页面
                    window.location.href = "/"
                })
            }
        }
    })
</script>

头部关键字搜索

 

 退出接口:

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喵俺第一专栏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值