一、前言
小编在前一篇博客中向大家介绍了使用单点登录的演变过程,最后一步的时候小编向大家展示了分布式架构。其中就用到了单点登录系统。这篇博客继续接上一篇博客,实现一下单点登录系统。
二、环境准备
Eclipse
Redis
三、单点登录流程图
这个是简单的单点登录流程图,就那淘宝来说,当我们进步淘宝首页的时候是没有登录的,点击登录的时候,会跳转到用户登录界面。此时的用户登录界面就是咱们SSO系统的一部分,根据登录的要求,会接收用户名和密码,然后根据用户名查询密码是否正确。
如果不正确就跳转到登录页,提示不正确;
如果正确就要进行以下步骤:
1. 生成一个uuid,作为token;
2. 把用户信息序列化存储到redis,存储的key为token,存储成功后,返回token;
3. 把token存储到cookie;
4. 判断是否有回调url,如果有,跳转到指定url;如果没有,跳转到系统首页;
四、实现过程
4.1 使用到的技术
Mybatis
Spring
Springmvc
Jedis
4.2 创建项目
创建Maven项目:
4.3 依赖的jar包
pom文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.dmsd</groupId> <artifactId>parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.dmsd</groupId> <artifactId>sso</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>com.dmsd</groupId> <artifactId>dao</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <scope>provided</scope> </dependency> <!-- Redis客户端 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> </dependencies> <!-- 添加tomcat插件 --> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>8084</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
4.4 整合SSM框架
这里的整合可以参考小编以前写的SSM整合博客。
4.5 登录逻辑实现
DAO层:
直接使用Mybatis逆向工程产生的代码。
Service层:
接收参数:用户名、密码。
校验密码是否正确,生成token,向redis中写入用户信息,把token写入cookie,返回SystemResult实体包含token。
参数:用户名、密码、HttpServletResponse、HttpServletRequest
返回值:TaotaoResult
@Service public class LoginServiceImpl implements LoginService { @Autowired private TbUserMapper userMapper; @Autowired private JedisClient jedisClient; @Value("${REDIS_SESSION_KEY}") private String REDIS_SESSION_KEY; @Value("${SESSION_EXPIRE}") private Integer SESSION_EXPIRE; @Override public SystemResult login(String username, String password, HttpServletRequest request, HttpServletResponse response) { //校验用户名密码是否正确 TbUserExample example = new TbUserExample(); Criteria criteria = example.createCriteria(); criteria.andUsernameEqualTo(username); List<TbUser> list = userMapper.selectByExample(example); //取用户信息 if (list == null || list.isEmpty()) { return TaotaoResult.build(400, "用户名或密码错误"); } TbUser user = list.get(0); //校验密码 if(!user.getPassword().equals(DigestUtils.md5DigestAsHex(password.getBytes()))) { return SystemResult.build(400, "用户名或密码错误"); } //登录成功 //生成token String token = UUID.randomUUID().toString(); //把用户信息写入redis //key:REDIS_SESSION:{TOKEN} //value:user转json user.setPassword(null); jedisClient.set(REDIS_SESSION_KEY + ":" + token, JsonUtils.objectToJson(user)); //设置session的过期时间 jedisClient.expire(REDIS_SESSION_KEY + ":" + token, SESSION_EXPIRE); //写cookie CookieUtils.setCookie(request, response, "TT_TOKEN", token); return SystemResult.ok(token); } }
Controller层:
请求的url:/user/login
接收参数:username、password
调用Service,返回taotaoResult对象。
响应json数据。
@Controller public class LoginController { @Autowired private LoginService loginService; @RequestMapping(value="/user/login", method=RequestMethod.POST) @ResponseBody public SystemResult login(String username, String password, HttpServletRequest request, HttpServletResponse response) { try { SystemResult result = loginService.login(username, password, request, response); return result; } catch (Exception e) { e.printStackTrace(); return SystemResult.build(500, ExceptionUtil.getStackTrace(e)); } } }
4.6 通过token查询用户信息
说明:根据token到redis查询用户信息,如果用户信息不存在说明session已经过期,返回400并提示用户session已经过期。如果查询到用户,返回用户信息,并且更新一下用户的过期时间。
请求url:/user/token/{token}
需要支持jsonp
返回:SystemResult
Dao层:
使用Jedis访问redis实现。
Service层:
参数:String token
根据token查询redis,查询到结果返回用户对象,更新过期时间。如果查询不到结果,返回Session已经过期,状态码400.
返回值:SystemResult
@Override public SystemResult getUserByToken(String token) { // 根据token取用户信息 String json = jedisClient.get(REDIS_SESSION_KEY + ":" + token); //判断是否查询到结果 if (StringUtils.isBlank(json)) { return SystemResult.build(400, "用户session已经过期"); } //把json转换成java对象 TbUser user = JsonUtils.jsonToPojo(json, TbUser.class); //更新session的过期时间 jedisClient.expire(REDIS_SESSION_KEY + ":" + token, SESSION_EXPIRE); return SystemResult.ok(user); }
Controller:
请求的url:/user/token/{token}
从url中取token的内容,调用Service取用户信息,返回TaotaoResult。(json数据)
@RequestMapping("/user/token/{token}") @ResponseBody public Object getUserByToken(@PathVariable String token, String callback) { try { TaotaoResult result = loginService.getUserByToken(token); //支持jsonp调用 if (StringUtils.isNotBlank(callback)) { MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result); mappingJacksonValue.setJsonpFunction(callback); return mappingJacksonValue; } return result; } catch (Exception e) { e.printStackTrace(); return SystemResult.build(500, ExceptionUtil.getStackTrace(e)); } }
首页系统中登录跳转的代码:
这里使用Ajax的跨域调用,先获取到存储在浏览器的cookie,然后使用Ajax的Jsonp来跨域调用用户信息,由于返回的是SystemResult的json形式,他包含了err、msg、data三个部分,所以要获取用户信息的时候就要通过data.data.username来获取。
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(); });
五、小结
这次的单点登录主要是使用redis完成的。redis真是一个非常好用的缓存技术。在很多方面都做了出色的表现。另外,就是这种产生分布式,把登录系统单独提取出来,这种思想有是很棒的。加油!
感谢博主,本文转自https://blog.csdn.net/kisscatforever/article/details/76468326