说明: 如果采用SESSION的方式实现用户的登录操作,由于nginx负载均衡的策略,用户可以访问不同的服务器.但是Session不能共享,所以导致用户频繁的登录. 用户的体验不好.
如果采用如下的配置,则必然会出现用户频繁登录的现象.
要求用户只需要登录一次,那么就可以访问其他的认证系统,无需用户再次登录.
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中, 用户只需一次登录就可以访问所有相互信任的应用系统。 这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的
1.当用户输入用户名和密码点击登录时,将请求发送给JT-WEB消费者服务器.
2.JT-WEB服务器将用户信息传递给JT-SSO单点登录系统完成数据校验.
3.如果登录成功,则动态生成密钥信息,将user数据转化为json.保存到redis中. 注意超时时间的设定.
4.JT-SSO将登录的凭证 传给JT-WEB服务器.
5.JT-WEB服务器将用户密钥TICKET信息保存到用户的cookie中 注意超时时间设定.
6.如果登录不成功,则直接返回错误信息即可.
/**
* 完成用户的登录操作
* url地址:http://www.jt.com/user/doLogin?r=0.8989367429030823
* 参数: username/password
* 返回值: SysResult对象 的JSON的数据.
*
* cookie.setMaxAge(-1); 关闭浏览器会话时删除
* cookie.setMaxAge(0); 立即删除cookie
* cookie.setMaxAge(100); cookie可以存储的时间单位是秒
*
* http://www.jt.com/saveUser/xxx
* cookie.setPath("/");
* cookie.setPath("/add");
*/
@RequestMapping("/doLogin")
@ResponseBody
public SysResult doLogin(User user, HttpServletResponse response){
//1.实现用户的登录操作!!!
String ticket = dubboUserService.doLogin(user);
//2.校验ticket是否有值.
if(StringUtils.isEmpty(ticket)){
//用户名或者密码错误
return SysResult.fail();
}
//3.如果用户的ticket不为null,则表示登录正确,需要将数据保存到cookie中
//Cookie要求 1.7天有效 2.要求cookie可以在jt.com的域名中共享 3.cookie权限 /
Cookie cookie = new Cookie("JT_TICKET",ticket);
cookie.setMaxAge(7*24*3600);
cookie.setDomain("jt.com"); //在jt.com中实现页面共享.
cookie.setPath("/"); //定于cookie的权限根目录有效
response.addCookie(cookie); //利用response将cookie保存到客户端中.
return SysResult.success();
}
* 业务:完成用户登录操作
* url地址: http://www.jt.com/user/doLogin?r=0.35842191622936337
* 参数:
* username: admin123
* password: admin123456
* 返回值: SysResult对象
*
* 业务具体实现:
* 1.校验用户名和密码是否正确
* 2.判断返回值结果是否为null 用户名和密码有误 返回201状态码
* 3.如果返回值结果不为null uuid保存到cookie中即可.
*
* Cookie知识介绍:
* 1.cookie.setPath("/") 根目录有效
* url1: www.jt.com/addUser
* url2: www.jt.com/user/addUser
*
* 2. cookie.setDomain("域名地址"); cookie在哪个域名中共享
* 例子1: cookie.setDomain("www.jt.com");
* 只有在www.jt.com的域名中有效
*
* cookie.setDomain("jt.com");
* 只有在jt.com结尾的域名中有效
*
*/
/**
* 1.获取用户信息校验数据库中是否有记录
* 2.有 开始执行单点登录流程
* 3.没有 直接返回null即可
* @param user
* @return
*/
@Override
public String doLogin(User user) { //username/password
//1.将明文加密
String md5Pass =
DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
user.setPassword(md5Pass);
QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
//根据对象中不为null的属性当做where条件.
User userDB = userMapper.selectOne(queryWrapper);
if(userDB == null){
//用户名或密码错误
return null;
}else{ //用户名和密码正确 实现单点登录操作
String ticket = UUID.randomUUID().toString();
//如果将数据保存到第三方 一般需要脱敏处理
userDB.setPassword("123456你信不??");
String userJSON = ObjectMapperUtil.toJSON(userDB);
jedisCluster.setex(ticket, 7*24*60*60, userJSON);
return ticket;
}
}
1
cookie 中存储的是 “jt-ticket” “ticket”
redis 中存储的事 “ticket” “userDB”
“jt-ticket” ---------------程序员指定的名称 一般与域名相关
“ticket” --------------- 在cookie 中当V,在redis 当K. 一般uuid
“userDB” ---------------指用户的信息,一般需要脱敏处理
@RequestMapping("/query/{ticket}")
public JSONPObject findUserByTicket(@PathVariable String ticket,
HttpServletResponse response,
String callback){
String userJSON = jedisCluster.get(ticket);
//1.lru算法清空数据 2.有可能cookie信息有误
if(StringUtils.isEmpty(userJSON)){
//2.应该删除cookie信息.
Cookie cookie = new Cookie("JT_TICKET", "");
cookie.setMaxAge(0);
cookie.setDomain("jt.com");
cookie.setPath("/");
response.addCookie(cookie);
return new JSONPObject(callback,SysResult.fail());
}
return new JSONPObject(callback,SysResult.success(userJSON));
}
package com.jt.util;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CookieUtil {
//1.新增cookie
public static void addCookie(HttpServletResponse response,String cookieName, String cookieValue, int seconds, String domain){
Cookie cookie = new Cookie(cookieName,cookieValue);
cookie.setMaxAge(seconds);
cookie.setDomain(domain);
cookie.setPath("/");
response.addCookie(cookie);
}
//2.根据name查询value的值
public static String getCookieValue(HttpServletRequest request,String cookieName){
Cookie[] cookies = request.getCookies();
if(cookies !=null && cookies.length >0){
for (Cookie cookie : cookies){
if(cookieName.equals(cookie.getName())){
return cookie.getValue();
}
}
}
return null;
}
//3.删除cookie
public static void deleteCookie(HttpServletResponse response,String cookieName,String domain){
addCookie(response,cookieName,"",0, domain);
}
}
如果用户点击退出操作, 首先应该删除Redis中的数据 其次删除Cookie中的数据 之后重定向到系统首页.
@RequestMapping("logout")
public String logout(HttpServletRequest request,HttpServletResponse response){
//1.根据JT_TICKET获取指定的ticket
String ticket = CookieUtil.getCookieValue(request,"JT_TICKET");
//2.判断ticket是否为null
if(!StringUtils.isEmpty(ticket)){
jedisCluster.del(ticket);
CookieUtil.deleteCookie(response,"JT_TICKET","jt.com");
}
return "redirect:/";
}
当用户进行敏感操作时,必须要求用户先登录之后才可以访问后端服务器. 例如京东商城…
使用技术:
1.AOP
2.拦截器 :拦截用户的请求
1 SpringMVC调用原理图
@Configuration //配置web.xml配置文件
public class MvcConfigurer implements WebMvcConfigurer{
//开启匹配后缀型配置,为了将来程序实现静态页面的跳转而准备的
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(true);
}
//添加拦截器配置
@Autowired
private UserInterceptor userInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userInterceptor).addPathPatterns("/cart/**","/order/**");
}
}
4. 拦截器业务实现
Spring版本升级 4 必须实现所有的方法 spring 5 只需要重写指定的方法即可.
/**
* 需求: 拦截/cart开头的所有的请求进行拦截.,并且校验用户是否登录.....
* 拦截器选择: preHandler
* 如何判断用户是否登录: 1.检查cookie信息 2.检查Redis中是否有记录.
* true : 请求应该放行
* false: 请求应该拦截 则配合重定向的语法实现页面跳转到登录页面 使得程序流转起来
*/
@Component //spring容器管理对象
public class UserInterceptor implements HandlerInterceptor {
@Autowired
private JedisCluster jedisCluster;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.判断用户是否登录 检查cookie是否有值
String ticket = CookieUtil.getCookieValue(request,"JT_TICKET");
//2.校验ticket
if(!StringUtils.isEmpty(ticket)){
//3.判断redis中是否有值.
if(jedisCluster.exists(ticket)){
//4.动态获取json信息
String userJSON = jedisCluster.get(ticket);
User user = ObjectMapperUtil.toObj(userJSON,User.class);
request.setAttribute("JT_USER",user);
return true;
}
}
response.sendRedirect("/user/login.html");
return false;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//销毁数据
request.removeAttribute("JT_USER");
}
}
4 动态获取UserId