企业级基于redis的SSO单点登录实现

创建redis服务+SSO服务

 

一、redis服务详解:

    1、为什么要创建redis服务?

        因为要对redis进行get和put操作,每个服务都有可能用到,所以提取成一个redis服务!

 

    2、redis服务的主要配置:

        配置redis(这里使用的lettuce)

         配置sentinel,这里作为集群在服务器部署

         配置eureka集群,进行注册(不解释,后面为RPC调用做准备)

企业级基于redis的SSO单点登录实现_第1张图片

 

3、定义put、get方法,并实现

企业级基于redis的SSO单点登录实现_第2张图片

4、controller

企业级基于redis的SSO单点登录实现_第3张图片

 

ok!redis就这样完成了,就这么简单,这里注意的传3个参数(key,value,失效时间)

 

二、真正的SSO服务

    1、SSO的主要配置:(这里就贴出feign,feign自带熔断,非常好用,SSO即是服务提供者也是消费者,因为要SSO要消费redis)

企业级基于redis的SSO单点登录实现_第4张图片

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);
}

转载收藏请标明出处

 

 

你可能感兴趣的:(企业级基于redis的SSO单点登录实现)