单点登录系统的实现

不同于传统的单机用户信息存放在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;

    }

}
原文地址:https://www.cnblogs.com/legion/p/9752459.html