zoukankan      html  css  js  c++  java
  • 单点登录系统的实现

    不同于传统的单机用户信息存放在session域中,单点登录系统创建专门的服务处理用户登录,相关信息存储在Redis中。

     1.定义服务的接口

    查询值是否可用
    http://YOURHOST/user/check/{param}/{type}
    
    type  可以是1,2,3,分别代表username,phone,email
    该接口主要目的是查询要注册的信息是否可用,get方法。
    例子
    http://YOURHOST/user/check/zhangsan/1
    {
    status: 200 //200 成功
    msg: "OK" // 返回信息消息
    data: false // 返回数据,true:数据可用,false:数据不可用
    }
    
    
    用户注册
    http://YOURHOST/user/register
    POST方法,参数username,password,phone,email
    返回值
    {
    status: 400
    msg: "注册失败. 请校验数据后请再提交数据."
    data: null
    }
    
    用户登录
    http://YOURHOST/user/login
    POST方法:参数:username,password
    返回值:
    {
    status: 200
    msg: "OK"
    data: "fe5cb546aeb3ce1bf37abcb08a40493e" //登录成功,返回token
    }
    
    通过token查询用户信息
    http://YOURHOST/user/token/{token}
    方法:GET,返回值
    {
    status: 200
    msg: "OK"
    data: "{"id":1,"username":"zhangzhijun","phone":"15800807944",
    "email":"420840806@qq.com","created":1414119176000,"updated":1414119179000}"
    }
    
    安全退出:
    http://YOURHOST/user/logout/{token}
    返回值
    {
    status: 200
    msg: "OK"
    data: ""
    }

    Controller层代码

    @Controller
    @RequestMapping("/user")
    public class UserController {
        @Autowired
        private UserService userService;
        
        @RequestMapping("/check/{param}/{type}")
        @ResponseBody
        public Object checkData(@PathVariable String param, @PathVariable Integer type, String callback) {
            
            TaotaoResult result = null;
            
            //参数有效性校验
            if (StringUtils.isBlank(param)) {
                result = TaotaoResult.build(400, "校验内容不能为空");
            }
            if (type == null) {
                result = TaotaoResult.build(400, "校验内容类型不能为空");
            }
            if (type != 1 && type != 2 && type != 3 ) {
                result = TaotaoResult.build(400, "校验内容类型错误");
            }
            //校验出错
            if (null != result) {
                if (null != callback) {
                    MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
                    mappingJacksonValue.setJsonpFunction(callback);
                    return mappingJacksonValue;
                } else {
                    return result; 
                }
            }
            //调用服务
            try {
                result = userService.checkData(param, type);
                
            } catch (Exception e) {
                result = TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
            }
            
            if (null != callback) {
                MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
                mappingJacksonValue.setJsonpFunction(callback);
                return mappingJacksonValue;
            } else {
                return result; 
            }
        }
        
        @RequestMapping(value="/register", method=RequestMethod.POST)
        @ResponseBody
        public TaotaoResult createUser(TbUser user) {
            
            try {
                TaotaoResult result = userService.createUser(user);
                return result;
            } catch (Exception e) {
                return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
            }
        }
        
        @RequestMapping(value="/login", method=RequestMethod.POST)
        @ResponseBody
        public TaotaoResult userLogin(String username, String password,
                HttpServletRequest request,HttpServletResponse response) {
            try {
                
                TaotaoResult result = userService.userLogin(username, password,request,response);
                return result;
            } catch (Exception e) {
                e.printStackTrace();
                return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
            }
        }
        
        @RequestMapping("/token/{token}")
        @ResponseBody
            public Object getUserByToken(@PathVariable String token, String callback) {
                TaotaoResult result = null;
                try {
                    result = userService.getUserByToken(token);
                } catch (Exception e) {
                    e.printStackTrace();
                    result = TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
                }
                
                //判断是否为jsonp调用
                if (StringUtils.isBlank(callback)) {
                    return result;
                } else {
                    MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
                    mappingJacksonValue.setJsonpFunction(callback);
                    return mappingJacksonValue;
                }
                
            }
    
        
    }

    Service层

    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private TbUserMapper userMapper;
        
        @Override
        public TaotaoResult checkData(String content, Integer type) {
            //创建查询条件
            TbUserExample example = new TbUserExample();
            Criteria criteria = example.createCriteria();
            //对数据进行校验:1、2、3分别代表username、phone、email
            //用户名校验
            if (1 == type) {
                criteria.andUsernameEqualTo(content);
            //电话校验
            } else if ( 2 == type) {
                criteria.andPhoneEqualTo(content);
            //email校验
            } else {
                criteria.andEmailEqualTo(content);
            }
            //执行查询
            List<TbUser> list = userMapper.selectByExample(example);
            if (list == null || list.size() == 0) {
                return TaotaoResult.ok(true);
            }
            return TaotaoResult.ok(false);
        }
    
        @Override
        public TaotaoResult createUser(TbUser user) {
            user.setUpdated(new Date());
            user.setCreated(new Date());
            //md5加密
            user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));
            userMapper.insert(user);
            return TaotaoResult.ok();
    
        }
        @Autowired
        private JedisClient jedisClient;
        @Value("${REDIS_USER_SESSION_KEY}")
        private String REDIS_USER_SESSION_KEY;
        @Override
        public TaotaoResult userLogin(String username, String password,
                HttpServletRequest request,HttpServletResponse response) {
            // TODO Auto-generated method stub
            TbUserExample example = new TbUserExample();
            Criteria criteria = example.createCriteria();
            criteria.andUsernameEqualTo(username);
            List<TbUser> list = userMapper.selectByExample(example);
            //如果没有此用户名
            if (null == list || list.size() == 0) {
                return TaotaoResult.build(400, "用户名或密码错误");
            }
            TbUser user = list.get(0);
            //比对密码
            if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) {
                return TaotaoResult.build(400, "用户名或密码错误");
            }
            //生成token
            String token = UUID.randomUUID().toString();
            //保存用户之前,把用户对象中的密码清空。
            user.setPassword(null);
            //把用户信息写入redis
            jedisClient.set(REDIS_USER_SESSION_KEY + ":" + token, JsonUtils.objectToJson(user));
            //设置session的过期时间
            jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, 1800);
            CookieUtils.setCookie(request, response, "TT_TOKEN", token);
            //返回token
            return TaotaoResult.ok(token);
    
        }
        //51d72eac-712d-46ed-8cfd-4b27a5eaf268
        @Override
        public TaotaoResult getUserByToken(String token) {
            
            //根据token从redis中查询用户信息
            String json = jedisClient.get(REDIS_USER_SESSION_KEY + ":" + token);
            //判断是否为空
            if (StringUtils.isBlank(json)) {
                return TaotaoResult.build(400, "此session已经过期,请重新登录");
            }
            //更新过期时间
            jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, 1800);
            //返回用户信息
            return TaotaoResult.ok(JsonUtils.jsonToPojo(json, TbUser.class));
        }
    
    
    
    }

    Dao层

    MySQL使用MyBatis生成的代码

    Redis的DAO层代码如下

    public class JedisClientImpl implements JedisClient {
        @Autowired
        private JedisPool jedisPool;
        @Override
        public String get(String key) {
            Jedis jedis = jedisPool.getResource();
            String string = jedis.get(key);
            jedis.close();
            return string;
        }
    
        @Override
        public String set(String key, String value) {
            Jedis jedis = jedisPool.getResource();
            String string = jedis.set(key, value);
            jedis.close();
            return string;
    
        }
    
        @Override
        public String hget(String hkey, String key) {
            Jedis jedis = jedisPool.getResource();
            String string = jedis.hget(hkey, key);
            jedis.close();
            return string;
    
        }
    
        @Override
        public Long hset(String hkey, String key, String value) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.hset(hkey, key, value);
            jedis.close();
            return result;
    
    
        }
    
        @Override
        public long incr(String key) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.incr(key);
            jedis.close();
            return result;
    
        }
    
        @Override
        public long expire(String key, int second) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.expire(key,second);
            jedis.close();
            return result;
    
        }
    
        @Override
        public long ttl(String key) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.ttl(key);
            jedis.close();
            return result;
    
        }
    
        @Override
        public long del(String key) {
            // TODO Auto-generated method stub
            Jedis jedis=jedisPool.getResource();
            long result=jedis.del(key);
            jedis.close();
            return result;
        }
    
    }

     除了服务,用户系统还需要单独提供注册页面和登录页面的服务。

    访问相关服务会请求转发到register.jsp和login.jsp。

    @Controller
    @RequestMapping("/page")
    public class PageController {
    
        @RequestMapping("/register")
        public String showRegister() {
            return "register";
        }
        
        @RequestMapping("/login")
        public String showLogin(String redirect,Model model) {
            model.addAttribute("redirect",redirect);
            return "login";
        }
    }
    在门户系统点击登录连接跳转到登录页面。
    登录成功后,跳转到门户系统的首页,在门户系统中需要从cookie中 把token取出来。

    所以必须在登录成功后把token写入cookie。并且cookie的值必须在系统之间能共享。

     设置Cookie:

    比如设置你的Cookie的domain为 .yourdomain.com

    设置Cookie的路径:/

    这里 使用的是locoalhost,就不需要设置domain了。

    上面UserServiceImpl中的userLogin方法将token和对应的用户信息放到Redis数据库中。

    并且设置Cookie,“TT_TOKEN"值就是token。

    其他用户可以登录的地方如何知道已经登录。

    如下面代码,页面加载的时候,获取Cookie中”TT_TOKEN“的值,也就是用户token,使用jsonp方法

    访问token服务。

    var TT = TAOTAO = {
        checkLogin : function(){
            var _ticket = $.cookie("TT_TOKEN");
            if(!_ticket){
                return ;
            }
            $.ajax({
                url : "http://localhost:8084/user/token/" + _ticket,
                dataType : "jsonp",
                type : "GET",
                success : function(data){
                    if(data.status == 200){
                        var username = data.data.username;
                        var html = username + ",欢迎来到淘淘!<a href="http://www.taotao.com/user/logout.html" class="link-logout">[退出]</a>";
                        $("#loginbar").html(html);
                    }
                }
            });
        }
    }
    
    $(function(){
        // 查看是否已经登录,如果已经登录查询登录信息
        TT.checkLogin();
    });

     

    使用拦截器拦截必须登录才可以使用的页面

    springmvc.xml中配置需要拦截的路径

        <!-- 拦截器配置 -->
        <mvc:interceptors>
            <mvc:interceptor>
                <!-- 拦截订单类请求 -->
                <mvc:mapping path="/item/**"/>
                <bean class="com.taotao.interceptor.LoginInterceptor"/>
            </mvc:interceptor>
        </mvc:interceptors>

    拦截器代码

    @Component
    public class LoginInterceptor implements HandlerInterceptor {
    
        @Autowired
        private UserService userService;
    
        @Override
        public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
                throws Exception {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
                throws Exception {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
            // 在Handler执行之前处理
            // 判断用户是否登录
            // 从cookie中取token
            String token = CookieUtils.getCookieValue(request, "TT_TOKEN");
            // 根据token换取用户信息,调用sso系统的接口。
            TbUser user = userService.getUserByToken(token);
            // 取不到用户信息
            if (null == user) {
                // 跳转到登录页面,把用户请求的url作为参数传递给登录页面。
                response.sendRedirect(
                        "http://localhost:8084/page/login" + "?redirect=" + request.getRequestURL());
                // 返回false
                return false;
            }
            // 取到用户信息,放行
            // 返回值决定handler是否执行。true:执行,false:不执行。
            return true;
    
        }
    
    }
  • 相关阅读:
    adodb.stream文件操作类详解
    Html中Label标记的作用和使用介绍
    正则表达式的威力轻松消除HTML代码
    只需一行代码就能让IE 6崩溃
    码农干货系列【17】Wind.js与Promise.js
    码农干货系列【3】割绳子(cut the rope)制作点滴:旋转(rotation)
    HTML5 Canvas开发者和读者的福音
    码农干货系列【8】世界上最简单的3D渲染(no webgl)
    码农干货系列【18】getting started with Promise.js(总)
    ProgressForm
  • 原文地址:https://www.cnblogs.com/legion/p/9752459.html
Copyright © 2011-2022 走看看