单点登陆解决方案以及代码分析与实现

来源:互联网 发布:淘宝无线端链接转换 编辑:程序博客网 时间:2024/06/15 13:10

1.什么是单点登陆

同一家公司不同应用使用同一套登陆系统叫做单点登陆系统

2.单点登陆的引出

 比如QQ号可以登陆很多游戏,而且这些游戏都是腾讯公司的,所以不再开发新的登陆模块,QQ就可以登陆多个应用.在写的过程中,我们注重用户体验,考虑用户体验的时候我们的用户名和密码的判断都是很精准的。没有用户名或者密码字样的出现。

3.代码实现

/*** 登陆* @param username 帐号* @param password 密码* @param ReturnUrl  回调地址* @param request   浏览器--服务器* @param response  服务器--浏览器* @param model  springmvc模型* @return  请求转发至登陆页面或重定向到回调地址*/@RequestMapping(value = "/Login.aspx", method = RequestMethod.POST)    public String login(String username, String password, String ReturnUrl, HttpServletRequest request,HttpServletResponse response, Model model) {        // 用户名不能为空        if (null != username) {            // 密码不能为空            if (null != password) {                // 用户名必须正确                Buyer buyer = buyerService.selectBuyerByUserName(username);                if (buyer != null) {                    // 密码必须正确                    if (buyer.getPassword().equals(encodingPassword(password))) {                        // TODO 密码不正确3次之后出现  验证码 不能为空 验证码 必须正确                        // 保存用户信息到远程session 单点登陆解决方案                        sessionProviderService.setAttributeForUserName                (RequestUtils.getCSESSIONID(request,response),buyer.getUsername());                        return "redirect:" + ReturnUrl;                    } else {                        model.addAttribute("error", "用户名必须正确");                    }                } else {                    model.addAttribute("error", "用户名必须正确");                }            } else {                model.addAttribute("error", "密码不能为空");            }        } else {            model.addAttribute("error", "用户名不能为空");        }        return "login";    }

同样在分布式架构中 在数据库中查询用户名也不是一个好的选择 ,因为不再是一个数据库,后期会搭建数据库集群,数据的存放是根据id存放的,在数据库中直接查询 用户名必须正确 buyerService.selectBuyerByUserName(username); 这一条SQL语句是十分浪费性能的,用户姓名是一个varchar字段,首先是不能建立比较好的索引策略,同时前台也无法传入正确的用户id。我们应该根据用户的id去查询用户,这样只会查询相应的数据库而不会全库扫描。
用户名查询解决方案

    @Override    public Buyer selectBuyerByUserName(String username) {        Buyer buyer = null;        // redis 中查找用户id        String id = jedis.hget("user", username);        if (id != username) {            // 用户查找对象            buyer = buyerDao.selectByPrimaryKey(Long.parseLong(id));        }        return buyer;    }

redis 中存放的是用户的类型是哈希 key 是用户名称 value 是用户的id 因为redis 是单线程并且是一个nosql数据库,查询速度非常的快,因此将用户存入redis 是在用户注册时候进行的。
redis 中命令如下 hmset username zhangsan 1

/** * 令牌工具类 * @author ZhuPengWei * @date    2017年11月7日 */public class RequestUtils {    /**     * 生成令牌     * @param request    浏览器-服务器     * @param response 服务器-浏览器       * @return  令牌     */    public static String getCSESSIONID(HttpServletRequest request, HttpServletResponse response) {        // 浏览器中有没有对应cookie CSSESIONID        Cookie[] cookies = request.getCookies();        for (Cookie cookie : cookies) {            if (Constant.COOKIE_CSSESIONID.equals(cookie.getName())) {                // 存在cookie                return cookie.getValue();            }        }        // 没有 创建一个令牌        String csession = UUID.randomUUID().toString().replaceAll("-", "");        Cookie cookie = new Cookie(Constant.COOKIE_CSSESIONID, csession);        cookie.setPath("/");        // 0立即失效 -1关闭浏览器失效        cookie.setMaxAge(-1);        // 写在浏览器之中        response.addCookie(cookie);        return csession;    }}

和大多公司单点登录解决方案一样,模拟request.getSession(),做出了一个假的session 存入了cookie中,
这样 服务器可以通过这个令牌在redis中获取数据或者设置数据了

/** * Session业务层接口 *     提供session *     获取session * @author ZhuPengWei * @date    2017年11月7日 */public interface SessionProviderService {    /**     * 设置远程session      * @param key  令牌     * @param userName 用户名     */    public void setAttributeForUserName(String key, String userName);    /**     * 从redis中取出用户名     * @param key 令牌     */    public String getAttributeUserNameByRedis(String key);}
package open.shopping.service.user;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import open.shopping.common.web.Constant;import redis.clients.jedis.Jedis;/** * Session业务层实现类 *     提供session *     获取session * @author ZhuPengWei * @date    2017年11月7日 */@Service("sessionProviderService")public class SessionProviderServiceImpl implements SessionProviderService {    @Autowired    private Jedis jedis;    @Override    public void setAttributeForUserName(String key, String userName) {        jedis.set(Constant.USER_SESSION + key, userName);        // 设置存活时间为30分钟        jedis.expire(Constant.USER_SESSION + key, 60 * 30);        jedis.close();    }    @Override    public String getAttributeUserNameByRedis(String key) {        String userName = jedis.get(Constant.USER_SESSION + key);        if (null != userName) {            // 设置存活时间为30分钟            jedis.expire(Constant.USER_SESSION + key, 60 * 30);        }        jedis.close();        return userName;    }}

是不是觉得单点登录其实也很简单。