SpringBoot系列-SSO整合

  • SpringBoot整合SSO教程
  • 前言
  • 一、技术介绍
    • 1.SSO是什么?
  • 二、使用步骤
    • 1.引入maven库
    • 2.具体使用示例
    • 3.配置文件
    • 4.单元测试
  • 总结

SpringBoot整合SSO教程

提示:如有疑问请私信联系、下方有源代码地址,请自行拿取


前言

网上SSO的框架很多,此篇文章使用的是自写的SSO来实现简单的登录授权功能,目的在于扩展性,权限这方面,自写扩展性会好点。


提示:以下是本篇文章正文内容,下面案例可供参考

一、技术介绍

1.SSO是什么?

单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的。

二、使用步骤

1.引入maven库

代码如下(示例):

     <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.1</version><relativePath/></parent><dependencies><dependencies><dependency><artifactId>hyh-boot-starter-redis</artifactId><groupId>com.hyh.redis</groupId><version>1.0.0</version></dependency></dependencies>

2.具体使用示例

ILogin接口:

package com.hyh.sso;import com.hyh.sso.po.LoginResult;/*** 登录接口** @Author: heyuhua* @Date: 2021/1/8 17:14*/
public interface ILogin {/*** 登录** @param account     用户名* @param password    密码* @param callbackUrl 用户验证回调URL* @return*/LoginResult login(String account, String password, String callbackUrl);}

登录状态枚举:

package com.hyh.sso;/*** 登录状态枚举** @Author: heyuhua* @Date: 2021/1/8 16:59*/
public enum LoginStatus {SUCCESS(1, "登录成功"), ING(0, "登录中"), FAIL(-1, "登录失败"),ERROR(-2, "登录异常"), CALLBACK_ERROR(-3, "登录回调异常"), ACCOUNT_LOCK(-4, "账户被锁定"),EXPIRE(-5,"登录用户已过期");/*** 登录状态码*/private int code;/*** 登录状态消息*/private String message;private LoginStatus(int code, String message) {this.code = code;this.message = message;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}

登录类型枚举:

package com.hyh.sso;/*** 登录类型** @Author: heyuhua* @Date: 2021/1/8 17:16*/
public enum LoginTypes {/*** 登入*/IN,/*** 登出*/OUT;}

登录常规接口:

package com.hyh.sso;package com.hyh.sso.service;import com.hyh.sso.ILogin;/*** 常规登录接口** @Author: heyuhua* @Date: 2021/1/8 17:54*/
public interface LoginService extends ILogin {}

登录接口实现:

package com.hyh.sso.service.impl;import com.alibaba.fastjson.JSON;
import com.hyh.sso.LoginStatus;
import com.hyh.sso.po.LoginResult;
import com.hyh.sso.po.LoginUser;
import com.hyh.sso.service.LoginService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;/*** 登录接口实现** @Author: heyuhua* @Date: 2021/1/8 17:56*/
@Service
public class LoginServiceImpl implements LoginService {private static final Logger LOG = LoggerFactory.getLogger(LoginServiceImpl.class);/*** rest接口请求模板*/private static RestTemplate restTemplate = new RestTemplate();@Overridepublic LoginResult login(String account, String password, String callbackUrl) {LoginResult loginResult = null;try {HttpHeaders headers = new HttpHeaders();//设置请求媒体数据类型headers.setContentType(MediaType.APPLICATION_JSON);//设置返回媒体数据类型headers.add("Accept", MediaType.APPLICATION_JSON.toString());HttpEntity<String> formEntity = new HttpEntity<String>(JSON.toJSONString(new LoginUser(account, password)), headers);loginResult = restTemplate.postForObject(callbackUrl, formEntity, LoginResult.class);} catch (Exception e) {LOG.error("login valid callback error", e);return new LoginResult(LoginStatus.CALLBACK_ERROR);}return loginResult == null ? new LoginResult(LoginStatus.ERROR) : loginResult;}}

登录用户对象:

package com.hyh.sso.po;/*** 登录用户对象** @Author: heyuhua* @Date: 2021/1/8 16:58*/
public class LoginUser {/*** 账号*/private String account;/*** 密码*/private String password;/*** 登录时间*/private String loginTime;public LoginUser(String account, String password) {this.account = account;this.password = password;}public LoginUser() {}public String getAccount() {return account;}public void setAccount(String account) {this.account = account;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getLoginTime() {return loginTime;}public void setLoginTime(String loginTime) {this.loginTime = loginTime;}
}

用户Token对象:

package com.hyh.sso.po;import com.hyh.utils.code.MD5;
import com.hyh.utils.common.StringUtils;import java.util.Calendar;/*** 用户Token对象** @Author: heyuhua* @Date: 2021/1/8 17:07*/
public class UserToken {/*** token*/private String token;/*** 过期时间*/private String expireTime;public UserToken(String token, String expireTime) {this.token = token;this.expireTime = expireTime;}public UserToken() {}public static UserToken getUserToken() {Calendar nowTime = Calendar.getInstance();nowTime.add(Calendar.MINUTE, 30);return new UserToken(MD5.getMD5String(StringUtils.ranStr(32)), String.valueOf(nowTime.getTimeInMillis()));}public String getToken() {return token;}public void setToken(String token) {this.token = token;}public String getExpireTime() {return expireTime;}public void setExpireTime(String expireTime) {this.expireTime = expireTime;}/*** 生成Token*/private String generateToken() {return MD5.getMD5String(StringUtils.ranStr(32));}
}

登录结果对象:

package com.hyh.sso.po;import com.hyh.sso.LoginStatus;
import com.hyh.sso.LoginTypes;/*** 登录结果对象* @Author: heyuhua* @Date: 2021/1/8 16:58*/
public class LoginResult {/*** 登录用户对象*/private LoginUser loginUser;/*** 登录用户令牌*/private UserToken userToken;/*** 登录状态*/private LoginStatus loginStatus;/*** 登录类型*/private LoginTypes loginTypes;public LoginResult(){}public LoginResult(LoginStatus loginStatus) {this.loginStatus = loginStatus;}public LoginUser getLoginUser() {return loginUser;}public void setLoginUser(LoginUser loginUser) {this.loginUser = loginUser;}public UserToken getUserToken() {return userToken;}public void setUserToken(UserToken userToken) {this.userToken = userToken;}public LoginStatus getLoginStatus() {return loginStatus;}public void setLoginStatus(LoginStatus loginStatus) {this.loginStatus = loginStatus;}public LoginTypes getLoginTypes() {return loginTypes;}public void setLoginTypes(LoginTypes loginTypes) {this.loginTypes = loginTypes;}@Overridepublic String toString() {return "LoginResult{" +"loginUser=" + loginUser +", userToken=" + userToken +", loginStatus=" + loginStatus +", loginTypes=" + loginTypes +'}';}
}

登录助手:

package com.hyh.sso.helper;import com.alibaba.fastjson.JSON;
import com.hyh.redis.helper.RedisHelper;
import com.hyh.sso.LoginStatus;
import com.hyh.sso.po.LoginResult;
import com.hyh.sso.po.LoginUser;
import com.hyh.sso.po.UserToken;
import com.hyh.sso.service.LoginService;
import com.hyh.utils.common.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;/*** 登录助手** @Author: heyuhua* @Date: 2021/1/8 17:13*/
@Component
public class LoginHelper {/*** 日志*/private static final Logger LOG = LoggerFactory.getLogger(LoginHelper.class);/*** 登录用户信息KEY*/private final String LOGIN_USER_KEY = "login:user:";/*** 登录用户TOKEN KEY*/private final String LOGIN_TOKEN_KEY = "login:token:";/*** 登录失败统计 KEY*/private final String LOGIN_FAIL_COUNT_KEY = "login:fail:count";/*** 登录失败最多允许次数*/private final long MAX_FAIL_COUNT = 5;/*** 登录服务*/@Resourceprivate LoginService loginService;/*** redis助手*/@Autowiredprivate RedisHelper redisHelper;/*** 登录** @param account     用户名* @param password    密码* @param callbackUrl 回调URL* @return*/public LoginResult login(String account, String password, String callbackUrl) {Assert.notNull(account, "account is null ");Assert.notNull(password, "password is null ");Assert.notNull(callbackUrl, "callbackUrl is null ");//判断账户是否多次登录失败被锁定String value = redisHelper.getStringValue(LOGIN_FAIL_COUNT_KEY + account);if (StringUtils.isNotBlank(value)) {Long loginFailCount = Long.parseLong(value);if (loginFailCount.longValue() >= MAX_FAIL_COUNT) {return new LoginResult(LoginStatus.ACCOUNT_LOCK);}}//登录操作LoginResult loginResult = loginService.login(account, password, callbackUrl);switch (loginResult.getLoginStatus()) {case SUCCESS://登录成功loginSuccess(loginResult);break;case FAIL://登录失败loginFail(loginResult);break;case ERROR:loginError(loginResult);//登录异常break;default:break;}return loginResult;}/*** 注销** @param account* @param token*/public void logout(String account, String token) {Assert.notNull(account, "account is null ");Assert.notNull(token, "token is null ");removeKey(account, token);}/*** 注销** @param token*/public void logout(String token) {Assert.notNull(token, "token is null ");removeKey(token);}/*** 获取登录用户** @param token* @return*/public LoginUser getLoginUser(String token) {Assert.notNull(token, "token is null ");String value = redisHelper.getStringValue(LOGIN_USER_KEY + token);if (StringUtils.isNotBlank(value)) {return JSON.parseObject(value, LoginUser.class);}return null;}/*** 移除 key** @param account* @param token*/private void removeKey(String account, String token) {redisHelper.del(LOGIN_FAIL_COUNT_KEY + account);redisHelper.del(LOGIN_TOKEN_KEY + account);redisHelper.del(LOGIN_USER_KEY + token);}/*** 移除 Key** @param token*/private void removeKey(String token) {redisHelper.del(LOGIN_USER_KEY + token);//其余的key到达过期时间自动过期}/*** 登录异常** @param loginResult*/private void loginError(LoginResult loginResult) {LOG.error("user 【" + loginResult.getLoginUser().getAccount() + "】 login error");}/*** 登录失败操作** @param loginResult*/private void loginFail(LoginResult loginResult) {String key = LOGIN_FAIL_COUNT_KEY + loginResult.getLoginUser();redisHelper.increment(key, 30 * 60 * 1000);}/*** 登录成功操作** @param loginResult*/private void loginSuccess(LoginResult loginResult) {LoginUser loginUser = loginResult.getLoginUser();loginUser.setLoginTime(String.valueOf(new Date().getTime()));UserToken userToken = UserToken.getUserToken();redisHelper.set(LOGIN_TOKEN_KEY + loginResult.getLoginUser().getAccount(), JSON.toJSONString(userToken), 30, TimeUnit.MINUTES);redisHelper.set(LOGIN_USER_KEY + userToken.getToken(), JSON.toJSONString(loginUser), 30, TimeUnit.MINUTES);redisHelper.del(LOGIN_FAIL_COUNT_KEY + loginResult.getLoginUser());}}

3.配置文件

代码如下(示例):

server:port: 8088spring:#redis配置redis:host: 192.168.6.134port: 30511password:

4.单元测试

测试代码如下(示例):

@Autowiredprivate LoginHelper loginHelper;@Testpublic void testLogin() {//测试时先开启HyhBootApplicationString account = "hyh";String password = "hyh-pwd";String cllbackUrl = "http://localhost:8088/hyh/login";//在com.hyh.core.web下可查看LoginResult loginResult = loginHelper.login(account, password, cllbackUrl);System.out.println("loginResult:" + loginResult.toString());}
//控制层代码@RequestMapping(value = "login", method = RequestMethod.POST)public LoginResult login(@RequestBody LoginUser loginUser) {Assert.notNull(loginUser.getAccount(), "account is null");Assert.notNull(loginUser.getPassword(), "password is null");LoginResult loginResult = new LoginResult(LoginStatus.SUCCESS);loginResult.setLoginUser(loginUser);//模拟直接返回登录成功return loginResult;}

总结

是不是感觉很简单?更多用法请点击下方查看源码,关注我带你揭秘更多高级用法

源码地址:点此查看源码.

SpringBoot SSO整合相关推荐

  1. 【SpringBoot】44、SpringBoot中整合JWT实现Token验证(整合篇)

    什么是JWT? Json web token (JWT),是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519),该 token 被设计为紧凑且安全的,特别适用于分 ...

  2. Docker 部署 SpringBoot 项目整合 Redis 镜像做访问计数Demo

    Docker 部署SpringBoot项目整合 Redis 镜像做访问计数Demo 最终效果如下 大概就几个步骤 1.安装 Docker CE 2.运行 Redis 镜像 3.Java 环境准备 4. ...

  3. springboot+security整合(1)

    说明 springboot 版本 2.0.3 源码地址:点击跳转 系列 springboot+security 整合(1) springboot+security 整合(2) springboot+s ...

  4. SpringBoot服务整合(整合邮件服务、定时调度、Actuator监控)

    在进行项目开发的时候经常会遇见以下的几个问题:需要进行邮件发送.定时的任务调度.系统的监控处理,实际上这些操 作都可以通过 SpringBoot 进行整合操作.2.1.SpringBoot 整合邮件服 ...

  5. springboot下整合各种配置文件

    本博是在springboot下整合其他中间件,比如,mq,redis,durid,日志...等等  以后遇到再更.springboot真是太便捷了,让我们赶紧涌入到springboot的怀抱吧. ap ...

  6. springboot项目整合mybatis

    SpringBoot项目整合mybatis 本章内容 使用 idea创建 SpringBoot项目 SpringBoot项目中配制 mybatis 框架 1 创建 SpringBoot项目 1.1 在 ...

  7. SpringBoot系列九:SpringBoot服务整合(整合邮件服务、定时调度、Actuator监控)

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 服务整合 2.背景 在进行项目开发的时候经常会遇见以下的几个问题:需要进行邮件发送.定时的任务调 ...

  8. SpringBoot项目整合Retrofit最佳实践,这才是最优雅的HTTP客户端工具!

    作者:六点半起床 juejin.im/post/6854573211426750472 大家都知道okhttp是一款由square公司开源的java版本http客户端工具.实际上,square公司还开 ...

  9. SpringBoot 2 整合 Spring Session 最简操作

    SpringBoot 2 整合 SpringSession 前言 Spring Session 介绍 SpringBoot 快速整合 Spring Session Spring Session 测试 ...

最新文章

  1. 讨论SELinux对系统安全的影响(转)
  2. python编程基础与应用-Python程序设计:从编程基础到专业应用
  3. mongodb 主从配置,带auth验证模式
  4. 自动发送邮件(整理版)
  5. 上海大华条码称代码_银豹收银之大华条码秤传称设置
  6. Neo4j: 迁移MySQL的数据到Neo4j
  7. 200901阶段一C++类和对象
  8. linkButton与Button 的click事件与onclientClick事件
  9. python函数装饰嵌套_python3--函数名本质,函数嵌套,闭包,装饰器
  10. VBA学习笔记(6)--抽取第一列中叫“虹虹”的个人信息
  11. 系统补丁安静安装参数
  12. 公差基本偏差代号_公差代号和公差带代号有什么区别?
  13. [渝粤教育] 内蒙古大学 微生物学 参考 资料
  14. 吴恩达深度学习作业之deepleraning_L1W2_h2
  15. 三级网络技术综合、应用题(2019、3)
  16. window设置oracle sid,window下改oracle_sid
  17. [0]SWM181-从零开发华芯微特MCU
  18. 网络安全特训之——网络信息安全攻防学习平台(基础关)
  19. 工作日志、周报的意义
  20. 搭建Web服务器-迅为IMX6ULL开发板

热门文章

  1. R语言 - 逻辑回归
  2. Orkut被XSS蠕虫击中
  3. TensorFlow学习--tf.summary.histogram与直方图仪表板/tensorboard_histograms
  4. MBA心路历程第一天 —— 开始行动
  5. XDC IO --- Output Delay Constraints(Vivado)
  6. WR | 西湖大学鞠峰组微纳塑料污染对人工湿地菌群与脱氮功能的影响
  7. 2021高考成绩查询微信预约推送,2021高考微信推送文案内容
  8. 后台版本趣味测试威信小程序源码下载支持自定义问题等等
  9. ubuntu软件开发必备东西
  10. 回顾 Exchange 2007 SCC 安装-供需要的人参考!