创建redis服务+SSO服务
一、redis服务详解:
1、为什么要创建redis服务?
因为要对redis进行get和put操作,每个服务都有可能用到,所以提取成一个redis服务!
2、redis服务的主要配置:
配置redis(这里使用的lettuce)
配置sentinel,这里作为集群在服务器部署
配置eureka集群,进行注册(不解释,后面为RPC调用做准备)
3、定义put、get方法,并实现
4、controller
ok!redis就这样完成了,就这么简单,这里注意的传3个参数(key,value,失效时间)
二、真正的SSO服务
1、SSO的主要配置:(这里就贴出feign,feign自带熔断,非常好用,SSO即是服务提供者也是消费者,因为要SSO要消费redis)
2、主要设计和实现的逻辑:
第一次用户请求登录,会从redis缓存中取数据,
1、如果redis中没有数据就从数据库中查询,查询出来之后再放入redis做为缓存,如果redis中有数据,则从redis取出,取的时候要做一个熔断处理,有时候会取失败!
2、这里对cookie不安全做了处理:
取出数据之后,也就是登录成功!生成一个token(UUID),将token当做key,账号当做value,存入redis,存redis时候做熔断处理!
3、真正的单点登录:
关于cookie,cookie在同域之间跨服务携带用户数据是不安全的,这里携带的是token,通过token获取账户,通过账户获取数据。
注意代码思想:这里业务逻辑层做登录处理,controller只做 登录成功!登陆失败处理!
@Controller
public class LoginController {
@Autowired
private RedisService redisService;
@Autowired
private LoginService loginService;
/**
* 跳转登录页面
*
* @return
*/
@RequestMapping(value = "login", method = RequestMethod.GET)
public String login(@RequestParam(value = "url", required = false) String url,
HttpServletRequest request,
Model model) {
//查看Cookie中是否有值
String token = CookieUtils.getCookieValue(request, "token");
//token不为空、可能是已经登录
if (StringUtils.isNotBlank(token)) {
//根据token拿到loginCode
String loginCode = redisService.get(token);
//如果loginCode不为空
if (StringUtils.isNotBlank(loginCode)) {
//根据loginCode获取,用户数据
String json = redisService.get(loginCode);
if (StringUtils.isNotBlank(json)) {
try {
//将获取到用户数据json格式,转成javaBean
TbSysUser tbSysUser = MapperUtils.json2pojo(json, TbSysUser.class);
//token loginCode TbSysUser 都不为空,说明已经登录了
//已登录
if (tbSysUser != null) {
//如果有url过来,重定向到url
if (StringUtils.isNotBlank(url)) {
return "redirect:" + url;
}
}
//将登录信息传到登录页面,没有url过来
model.addAttribute("tbSysUser", tbSysUser);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return "login";
}
/**
* @param loginCode 邮箱
* @param password 密码
* @param url 返回登录之前的页面路径 @RequestParam(value = "url", required = false)
* @param request 请求
* @param response 响应
* @param redirectAttributes 重定向的参数
* @return 返回值
*/
@RequestMapping(value = "login", method = RequestMethod.POST)
public String login(@RequestParam(value = "loginCode", required = true) String loginCode,
@RequestParam(value = "password", required = true) String password,
@RequestParam(value = "url", required = false) String url,
HttpServletRequest request,
HttpServletResponse response,
RedirectAttributes redirectAttributes) {
//执行登录方法
TbSysUser tbSysUser = loginService.login(loginCode, password);
//生成token当做key存到redis中
String token = UUID.randomUUID().toString();
//登陆失败
if (tbSysUser == null) {
//重定向的方法
redirectAttributes.addFlashAttribute("message", "账号密码失败!");
}
//登录成功
else {
//将token当成key loginCode当做value 存入redis
String result = redisService.put(token, loginCode, 60 * 60 * 24);
//如果存入redis成功,也就是result不为空,redis的put方法返回ok
if (StringUtils.isNotBlank(result) && "ok".equals(result)) {
//将token放入Cookie
CookieUtils.setCookie(request, response, "token", token, 60 * 60 * 24);
//重定向到来的页面(如果是其他服务来登录,就返回过去)
return "redirect:" + url;
}
//假如存redis失败,被熔断处理
else {
redirectAttributes.addFlashAttribute("message", "服务器异常,请稍后再试!");
}
}
//登录失败重定向到login方法,执行RequestMapping("login")跳转页面
return "redirect:/login";
}
}
package com.funtl.itoken.service.sso.service.Impl;
import com.funtl.itoken.common.domain.TbSysUser;
import com.funtl.itoken.common.utils.MapperUtils;
import com.funtl.itoken.service.sso.mapper.TbSysUserMapper;
import com.funtl.itoken.service.sso.service.LoginService;
import com.funtl.itoken.service.sso.service.consumer.RedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import tk.mybatis.mapper.entity.Example;
@Service
public class LoginServiceImpl implements LoginService {
//slf4j的logger
private static final Logger logger = LoggerFactory.getLogger(LoginServiceImpl.class);
@Autowired
private RedisService redisService;
@Autowired
private TbSysUserMapper tbSysUserMapper;
@Override
public TbSysUser login(String loginCode, String planPassword) {
TbSysUser tbSysUser = null;
//从redis里面取loginCode
String json = redisService.get(loginCode);
//缓存中没有数据
if (json == null) {
//从数据库中查询
Example example = new Example(TbSysUser.class);
//将查询的字段添加到example,创建???
example.createCriteria().andEqualTo("loginCode", loginCode);
//根据用户名查询数据
tbSysUser = tbSysUserMapper.selectOneByExample(example);
//将参数(planPassword)密码进行MD5加密
String password = DigestUtils.md5DigestAsHex(planPassword.getBytes());
//如果加密的密码 对比 数据库中的密码(判断密码是否正确)
if (tbSysUser != null && password.equals(tbSysUser.getPassword())) {
try {
//将查出来的数据、放入redis中
redisService.put(loginCode, MapperUtils.obj2json(tbSysUser), 60 * 60 * 24);
} catch (Exception e) {
e.printStackTrace();
}
return tbSysUser;
} else {
//密码错误,直接返回null,controller来判断,假如tbSysUser为null,直接给错误提示信息!!!
return null;
}
}
//缓存中有数据
else {
try {
//取redis中的数据时候,假如发生错,直接走熔断
tbSysUser = MapperUtils.json2pojo(json, TbSysUser.class);
} catch (Exception e) {
logger.warn("触发熔断:{}", e.getMessage());
}
}
return tbSysUser;
}
}
package com.funtl.itoken.service.sso.service.consumer;
import com.funtl.itoken.service.sso.service.consumer.fallback.RedisServiceFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
//指定消费的服务,定义回路方法
@FeignClient(value = "itoken-service-redis", fallback = RedisServiceFallback.class)
public interface RedisService {
/**
* redis的接口--put
*
* @param key
* @param value
* @param seconds
* @return
*/
@RequestMapping(value = "put", method = RequestMethod.POST)
public String put(@RequestParam(value = "key") String key,
@RequestParam(value = "value") String value,
@RequestParam(value = "seconds") long seconds);
/**
* redis的接口--get
*
* @param key
* @return
*/
@RequestMapping(value = "get", method = RequestMethod.GET)
public String get(@RequestParam(value = "key") String key);
}