TT_单点登录系统SSO实现

1. 计划

1、 实现单点登录系统

2、 实现用户的登录功能

3、 实现用户的注册功能

2. 单点登录系统分析

2.1. 什么是SSO

SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。

2.2. 原来的登录逻辑实现

2.2.1. 问题

单台tomcat以上实现是没有任何问题的,但是我们现在是集群的tomcat就会存在session共享问题。只要解决session共享问题,登录问题即可解决。

 

每个系统都有自己的session,不能统一。

2.2.2. 解决session共享问方案

1、 tomcatsession复制

优点:不需要额外开发,只需要搭建tomcat集群即可。

缺点tomcat 是全局session复制,集群内每个tomcatsession完全同步(也就是任何时候都完全一样的在大规模应用的时候,用户过多,集群内tomcat数量过多,session的全局复制会导致集群性能下降 因此,tomcat的数量不太多,5个以下为好

2、 实现单点登录系统,提供服务接口。把session数据存放在redis。

Redis可以设置key生存时间、访问速度快效率高。

优点:redis存取速度快,不会出现多个节点session复制的问题。效率高。

缺点:需要程序员开发。

 

2.3. 单点登录系统的流程



3. SSO开发

3.1. 系统架构

3.2. 开发SSO服务

3.2.1. 创建sso服务工程

 

3.2.2. Pom.xml

Pom.xml文件参考taotao-rest工程的pom文件。

3.2.3. 服务开发

a) 登录接口

b) 注册接口

c) 查询接口

d) 退出登录接口

开发的流程:

1、 确定流程、确定接口内容

2、 提供接口文档

a) 接口地址

b) 入参说明

c) 接口访问方式,get、post

d) 结果输出,说明格式

e) 接口调用的示例

3、 约定联合测试的时间点

4、 发布上线

参考:SSO接口文档.docx

3.3. 开发注册接口

1、 需要对用户提交的数据做校验

2、 对密码做md5加密

3、 对报错异常要做处理

 

3.3.1. 数据校验接口

1. Mapper

tb_user表进行单表查询。使用逆向工程生成的mapper即可。

 

2. Service

[java]  view plain  copy
  1. @Service  
  2. public class UserRegisterServiceImpl implements UserRegisterService {  
  3. @Autowired  
  4. private TbUserMapper userMapper;  
  5.    
  6. @Override  
  7. public TaotaoResult checkInfo(String value, String type) throws Exception {  
  8. boolean result = false;  
  9. //type为类型,可选参数1、2、3分别代表username、phone、email  
  10. if ("1".equals(type)) {  
  11. result = checkUserName(value);  
  12. else if ("2".equals(type)) {  
  13. result = checkPhone(value);  
  14. else if ("3".equals(type)) {  
  15. result = checkEmail(value);  
  16. }  
  17. //返回结果  
  18. if (result) {  
  19. return TaotaoResult.ok(result);  
  20. }  
  21. return TaotaoResult.build(201"此数值已经存在");  
  22. }  
  23. private boolean checkUserName(String userName) throws Exception {  
  24. //创建查询条件  
  25. TbUserExample example = new TbUserExample();  
  26. Criteria criteria = example.createCriteria();  
  27. criteria.andUsernameEqualTo(userName);  
  28. List<TbUser> list = userMapper.selectByExample(example);  
  29. //判断结果中是否存在  
  30. if (list == null || list.isEmpty()) {  
  31. return true;  
  32. }  
  33. return false;  
  34. }  
  35. private boolean checkPhone(String phone) throws Exception {  
  36. //创建查询条件  
  37. TbUserExample example = new TbUserExample();  
  38. Criteria criteria = example.createCriteria();  
  39. criteria.andPhoneEqualTo(phone);  
  40. List<TbUser> list = userMapper.selectByExample(example);  
  41. //判断结果中是否存在  
  42. if (list == null || list.isEmpty()) {  
  43. return true;  
  44. }  
  45. return false;  
  46. }  
  47. private boolean checkEmail(String email) throws Exception {  
  48. //创建查询条件  
  49. TbUserExample example = new TbUserExample();  
  50. Criteria criteria = example.createCriteria();  
  51. criteria.andEmailEqualTo(email);  
  52. List<TbUser> list = userMapper.selectByExample(example);  
  53. //判断结果中是否存在  
  54. if (list == null || list.isEmpty()) {  
  55. return true;  
  56. }  
  57. return false;  
  58. }  
  59. }  


3. Controller 

[java]  view plain  copy
  1. @Controller  
  2. @RequestMapping("/user")  
  3. public class UserRegisterController {  
  4.    
  5. @Autowired  
  6. private UserRegisterService userRegisterService;  
  7. @RequestMapping("/check/{param}/{type}")  
  8. @ResponseBody  
  9. public Object checkInfo(@PathVariable String param, @PathVariable String type, String callback) {  
  10. TaotaoResult result = null;  
  11. try {  
  12. result = userRegisterService.checkInfo(param, type);  
  13. catch (Exception e) {  
  14. // TODO Auto-generated catch block  
  15. e.printStackTrace();  
  16. result = TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));  
  17. }  
  18. //支持jsonp  
  19. if (!StringUtils.isBlank(callback)) {  
  20. MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);  
  21. mappingJacksonValue.setJsonpFunction(callback);  
  22. return mappingJacksonValue;  
  23. }  
  24. return result;  
  25. }  
  26. }  


3.3.2. 用户注册接口 
1. service

[java]  view plain  copy
  1. @Override  
  2. public TaotaoResult register(TbUser user) throws Exception {  
  3. //有效性验证  
  4. if (StringUtils.isBlank(user.getUsername())) {  
  5. return TaotaoResult.build(400"用户名不能为空");  
  6. }  
  7. if (StringUtils.isBlank(user.getPassword())) {  
  8. return TaotaoResult.build(400"密码不能为空");  
  9. }  
  10. if (StringUtils.isBlank(user.getPhone())) {  
  11. return TaotaoResult.build(400"手机不能为空");  
  12. }  
  13. //转换md5  
  14. user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));  
  15. //完善user信息  
  16. user.setCreated(new Date());  
  17. user.setUpdated(new Date());  
  18. //添加到数据库  
  19. userMapper.insert(user);  
  20. return TaotaoResult.ok();  
  21. }  


2. Controller 

[java]  view plain  copy
  1. @RequestMapping(value="/register", method=RequestMethod.POST)  
  2. public TaotaoResult register(TbUser user) {  
  3. TaotaoResult taotaoResult = null;  
  4. try {  
  5. taotaoResult = userRegisterService.register(user);  
  6. catch (Exception e) {  
  7. // TODO Auto-generated catch block  
  8. e.printStackTrace();  
  9. return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));  
  10. }  
  11. return taotaoResult;  
  12. }  


3.4. 开发登录接口 

3.4.1. Service

[java]  view plain  copy
  1. @Service  
  2. public class UserLoginServiceImpl implements UserLoginService {  
  3.    
  4. @Autowired  
  5. private TbUserMapper userMapper;  
  6. @Autowired  
  7. private JedisCluster jedisCluster;  
  8. @Value("${USER_TOKEN_KEY}")  
  9. private String USER_TOKEN_KEY;  
  10. @Value("${SESSION_EXPIRE_TIME}")  
  11. private Integer SESSION_EXPIRE_TIME;  
  12. @Override  
  13. public TaotaoResult login(String username, String password) throws Exception {  
  14. //根据用户名查询用户信息  
  15. TbUserExample example = new TbUserExample();  
  16. Criteria criteria = example.createCriteria();  
  17. criteria.andUsernameEqualTo(username);  
  18. List<TbUser> list = userMapper.selectByExample(example);  
  19. if (null == list || list.isEmpty()) {  
  20. return TaotaoResult.build(400"用户不存在");  
  21. }  
  22. //核对密码  
  23. TbUser user = list.get(0);  
  24. if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) {  
  25. return TaotaoResult.build(400"密码错误");  
  26. }  
  27. //登录成功,把用户信息写入redis  
  28. //生成一个用户token  
  29. String token = UUID.randomUUID().toString();  
  30. jedisCluster.set(USER_TOKEN_KEY + ":" + token, JsonUtils.objectToJson(user));  
  31. //设置session过期时间  
  32. jedisCluster.expire(USER_TOKEN_KEY + ":" + token, SESSION_EXPIRE_TIME);  
  33. return TaotaoResult.ok(token);  
  34. }  
  35.    
  36. }  


3.4.2. Controller 

[java]  view plain  copy
  1. @Controller  
  2. @RequestMapping("/user")  
  3. public class UserLoginController {  
  4.    
  5. @Autowired  
  6. private UserLoginService userLoginService;  
  7. @RequestMapping(value="/login", method=RequestMethod.POST)  
  8. @ResponseBody  
  9. public TaotaoResult login(String username, String password) {  
  10. TaotaoResult result = null;  
  11. try {  
  12. result = userLoginService.login(username, password);  
  13. catch (Exception e) {  
  14. // TODO Auto-generated catch block  
  15. e.printStackTrace();  
  16. return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));  
  17. }  
  18. return result;  
  19. }  
  20. }  


3.5. 根据token查询用户 

3.5.1. Service

[java]  view plain  copy
  1. @Service  
  2. public class UserTokenServiceImpl implements UserTokenService {  
  3.    
  4. @Autowired  
  5. private JedisCluster jedisCluster;  
  6. @Value("${USER_TOKEN_KEY}")  
  7. private String USER_TOKEN_KEY;  
  8. @Value("${SESSION_EXPIRE_TIME}")  
  9. private Integer SESSION_EXPIRE_TIME;  
  10. /** 
  11.  * 根据token取用户信息 
  12.  * <p>Title: getUserByToken</p> 
  13.  * <p>Description: </p> 
  14.  * @param token 
  15.  * @return 
  16.  * @throws Exception 
  17.  * @see com.taotao.sso.service.UserTokenService#getUserByToken(java.lang.String) 
  18.  */  
  19. @Override  
  20. public TaotaoResult getUserByToken(String token) throws Exception {  
  21. //从redis中取用户信息  
  22. String userJson = jedisCluster.get(USER_TOKEN_KEY + ":" + token);  
  23. if (StringUtils.isBlank(userJson)) {  
  24. return TaotaoResult.build(400"该用户已过期");  
  25. }  
  26. //把json转换成user对象  
  27. TbUser user = JsonUtils.jsonToPojo(userJson, TbUser.class);  
  28. //更新用户有效期  
  29. jedisCluster.expire(USER_TOKEN_KEY + ":" + token, SESSION_EXPIRE_TIME);  
  30. return TaotaoResult.ok(user);  
  31. }  
  32.    
  33. }  


3.5.2. Controller 

[java]  view plain  copy
  1. @Controller  
  2. @RequestMapping("/user")  
  3. public class UserTokenController {  
  4.    
  5. @Autowired  
  6. private UserTokenService userTokenService;  
  7. @RequestMapping("/token/{token}")  
  8. @ResponseBody  
  9. public Object getUserByToken(@PathVariable String token, String callback) {  
  10. TaotaoResult result = null;  
  11. try {  
  12. result = userTokenService.getUserByToken(token);  
  13. catch (Exception e) {  
  14. e.printStackTrace();  
  15. result = TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));  
  16. }  
  17. //判断是否为jsonp调用  
  18. if (!StringUtils.isBlank(callback)) {  
  19. MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);  
  20. mappingJacksonValue.setJsonpFunction(callback);  
  21. return mappingJacksonValue;  
  22. }  
  23. return result;  
  24. }  
  25. }  


4. 用户 注册 

4.1. 需求分析

用户注册时需要调用taotao-sso的服务检查用户的有效性以及用户、手机是否存在。检查通过后提交表单至taotao-portalcontroller,由taotao-portal调用taotao-sso的服务注册用户。

4.2. 有效性检查

 

4.3. 用户注册实现

4.3.1. Service

[java]  view plain  copy
  1. @Service  
  2. public class UserServiceImpl implements UserService {  
  3.    
  4. @Value("${SSO_BASE_URL}")  
  5. private String SSO_BASE_URL;  
  6. @Value("${REGISTER_USER_URL}")  
  7. private String REGISTER_USER_URL;  
  8. @Override  
  9. public TaotaoResult register(TbUser user) {  
  10. //请求参数  
  11. Map<String, String> param = new HashMap<>();  
  12. param.put("username", user.getUsername());  
  13. param.put("password", user.getPassword());  
  14. param.put("phone", user.getPhone());  
  15. param.put("email", user.getEmail());  
  16. //提交用户信息  
  17. String stringResult = HttpClientUtil.doPost(SSO_BASE_URL + REGISTER_USER_URL, param);  
  18. TaotaoResult result = TaotaoResult.format(stringResult);  
  19. return result;  
  20. }  
  21.    
  22. }  


4.3.2. Controller 

[java]  view plain  copy
  1. @RequestMapping("/doregister")  
  2. @ResponseBody  
  3. public TaotaoResult doRegister(TbUser user) throws Exception {  
  4. TaotaoResult result = userService.register(user);  
  5. return result;  
  6. }  


5. 用户登录

5.1. 需求

前台页面提交用户名、密码至taotao-portal,由taotao-poratl调用sso系统的服务做登录处理。登录成功后返回用户token数据。taotao-portaltoken写入cookie,返回登录成功。页面接收到成功信息后,跳转至商品列表页面。商品列表页面从cookie中取出token信息,根据token查询用户并显示到首页。

 

5.2. Service

[java]  view plain  copy
  1. public TaotaoResult login(String username, String password,  
  2. HttpServletRequest request, HttpServletResponse response) {  
  3. //请求参数  
  4. Map<String, String> param = new HashMap<>();  
  5. param.put("username", username);  
  6. param.put("password", password);  
  7. //登录处理  
  8. String stringResult = HttpClientUtil.doPost(REGISTER_USER_URL + USER_LOGIN_URL, param);  
  9. TaotaoResult result = TaotaoResult.format(stringResult);  
  10. //登录出错  
  11. if (result.getStatus() != 200) {  
  12. return result;  
  13. }  
  14. //登录成功后把取token信息,并写入cookie  
  15. String token = (String) result.getData();  
  16. //写入cookie  
  17. CookieUtils.setCookie(request, response, "TT_TOKEN", token);  
  18. //返回成功  
  19. return result;  
  20. }  


5.3. Controller 

[java]  view plain  copy
  1. /** 
  2.  * 登录处理 
  3.  * <p>Title: doLogin</p> 
  4.  * <p>Description: </p> 
  5.  * @param username 
  6.  * @param password 
  7.  * @param request 
  8.  * @param response 
  9.  * @return 
  10.  */  
  11. @RequestMapping("/dologin")  
  12. @ResponseBody  
  13. public TaotaoResult doLogin(String username, String password,  
  14. HttpServletRequest request, HttpServletResponse response) {  
  15. TaotaoResult result = userService.login(username, password, request, response);  
  16. return result;  
  17. }  

5.4. 显示当前的用户名

Taotao.js

[javascript]  view plain  copy
  1. var TT = TAOTAO = {  
  2. checkLogin : function(){  
  3. var _ticket = $.cookie("TT_TOKEN");  
  4. if(!_ticket){  
  5. return ;  
  6. }  
  7. $.ajax({  
  8. url : "http://localhost:8084/user/token/" + _ticket,  
  9. dataType : "jsonp",  
  10. type : "GET",  
  11. success : function(data){  
  12. if(data.status == 200){  
  13. var username = data.data.username;  
  14. var html = username + ",欢迎来到淘淘!<a href=\"http://localhost:8084/user/logout.html\" class=\"link-logout\">[退出]</a>";  
  15. $("#loginbar").html(html);  
  16. }  
  17. }  
  18. });  
  19. }  
  20. }  
  21.    
  22. $(function(){  
  23. // 查看是否已经登录,如果已经登录查询登录信息  
  24. TT.checkLogin();  
  25. });  

6. 拦截器

6.1. 拦截器流程

 

 

6.2. 根据token取用户信息

需要在UserSevice中添加一个根据token取用户对象的方法。

[java]  view plain  copy
  1. @Override  
  2. public TbUser getUserByToken(String token) {  
  3. //根据token取用户信息  
  4. String stringResult = HttpClientUtil.doGet(SSO_BASE_URL + GET_USER_BY_TOKEN_URL + token);  
  5. //把返回结果转换成java对象  
  6. TaotaoResult taotaoResult = TaotaoResult.formatToPojo(stringResult, TbUser.class);  
  7. //判断返回状态  
  8. if (taotaoResult.getStatus() == 200) {  
  9. //取User对象  
  10. TbUser user = (TbUser) taotaoResult.getData();  
  11. return user;  
  12. }  
  13. return null;  
  14. }  

6.3. 用户登录功能实现登录后跳转

6.3.1. 接收跳转url参数

 

6.3.2. 页面保存此url

login.jsp

 

 

6.4. 拦截器实现

[java]  view plain  copy
  1. /** 
  2.  * 在提交订单之前拦截,判断用户是否登录 
  3.  * <p>Title: OrderInterceptor</p> 
  4.  * <p>Description: </p> 
  5.  * <p>Company: www.itcast.com</p>  
  6.  * @author入云龙 
  7.  * @date2015年8月6日上午10:43:04 
  8.  * @version 1.0 
  9.  */  
  10. public class OrderInterceptor implements HandlerInterceptor {  
  11. @Autowired  
  12. private UserService userService;  
  13. @Override  
  14. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  15. throws Exception {  
  16. //取用户token  
  17. String token = CookieUtils.getCookieValue(request, "TT_TOKEN");  
  18. //判断是否为空  
  19. if (StringUtils.isBlank(token)) {  
  20. //如果为空就是未登录状态  
  21. //跳转到登录页面  
  22. response.sendRedirect(userService.getLoginUrl() + "?redirect=" + getBaseURL(request));  
  23. return false;  
  24. else {  
  25. //如果能取到token说明用户可能已经登录  
  26. //从sso中取用户信息,判断用户是否登录  
  27. TbUser user = userService.getUserByToken(token);  
  28. //判断用户是否过期  
  29. if (user == null) {  
  30. //跳转到登录页面  
  31. response.sendRedirect(userService.getLoginUrl() + "?redirect=" + getBaseURL(request));  
  32. return false;  
  33. else {  
  34. //用户已经登录,把用户信息放到request中  
  35. request.setAttribute("user", user);  
  36. }  
  37. }  
  38. //放行  
  39. return true;  
  40. }  
  41.    
  42. @Override  
  43. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,  
  44. ModelAndView modelAndView) throws Exception {  
  45. // TODO Auto-generated method stub  
  46.    
  47. }  
  48.    
  49. @Override  
  50. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)  
  51. throws Exception {  
  52. // TODO Auto-generated method stub  
  53.    
  54. }  
  55. private String getBaseURL(HttpServletRequest request) {  
  56. String url = request.getScheme()  
  57. "://"   
  58. + request.getServerName()  
  59. ":"  
  60. + request.getServerPort()  
  61. + request.getContextPath()  
  62. + request.getRequestURI();  
  63. return url;  
  64. }  
  65.    
  66. }  


6.5. 配置拦截器 

springmvc中添加如下配置:

[html]  view plain  copy
  1. <!-- 拦截器配置 -->  
  2. <mvc:interceptors>  
  3. <mvc:interceptor>  
  4. <!-- 拦截订单类请求 -->  
  5. <mvc:mapping path="/order/**"/>  
  6. <bean class="com.taotao.portal.interceptor.OrderInterceptor"/>  
  7. </mvc:interceptor>  
  8. </mvc:interceptors>  



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值