SpringBoot SSO轻松实现(附demo)
前言
网上SSO的框架很多,此篇文章使用的是自写的SSO来实现简单的登录授权功能,目的在于扩展性,权限这方面,自写扩展性会好点。
提示:以下是本篇文章正文内容,下面案例可供参考
一、技术介绍
1.SSO是什么?
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的。
二、使用步骤
1.引入maven库
代码如下(示例):
org.springframework.boot spring-boot-starter-parent 2.4.1 hyh-boot-starter-redis com.hyh.redis 1.0.0
2.具体使用示例
ILogin接口:
packagecom.hyh.sso;
importcom.hyh.sso.po.LoginResult;
/**
*登录接口
*
*@Author:heyuhua
*@Date:2021/1/817:14
*/
publicinterfaceILogin{
/**
*登录
*
*@paramaccount用户名
*@parampassword密码
*@paramcallbackUrl用户验证回调URL
*@return
*/
LoginResultlogin(Stringaccount,Stringpassword,StringcallbackUrl);
}
登录状态枚举:
packagecom.hyh.sso;
/**
*登录状态枚举
*
*@Author:heyuhua
*@Date:2021/1/816:59
*/
publicenumLoginStatus{
SUCCESS(1,"登录成功"),ING(0,"登录中"),FAIL(-1,"登录失败"),
ERROR(-2,"登录异常"),CALLBACK_ERROR(-3,"登录回调异常"),ACCOUNT_LOCK(-4,"账户被锁定"),
EXPIRE(-5,"登录用户已过期");
/**
*登录状态码
*/
privateintcode;
/**
*登录状态消息
*/
privateStringmessage;
privateLoginStatus(intcode,Stringmessage){
this.code=code;
this.message=message;
}
publicintgetCode(){
returncode;
}
publicvoidsetCode(intcode){
this.code=code;
}
publicStringgetMessage(){
returnmessage;
}
publicvoidsetMessage(Stringmessage){
this.message=message;
}
}
登录类型枚举:
packagecom.hyh.sso;
/**
*登录类型
*
*@Author:heyuhua
*@Date:2021/1/817:16
*/
publicenumLoginTypes{
/**
*登入
*/
IN,
/**
*登出
*/
OUT;
}
登录常规接口:
packagecom.hyh.sso;
packagecom.hyh.sso.service;
importcom.hyh.sso.ILogin;
/**
*常规登录接口
*
*@Author:heyuhua
*@Date:2021/1/817:54
*/
publicinterfaceLoginServiceextendsILogin{
}
登录接口实现:
packagecom.hyh.sso.service.impl;
importcom.alibaba.fastjson.JSON;
importcom.hyh.sso.LoginStatus;
importcom.hyh.sso.po.LoginResult;
importcom.hyh.sso.po.LoginUser;
importcom.hyh.sso.service.LoginService;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.http.HttpEntity;
importorg.springframework.http.HttpHeaders;
importorg.springframework.http.MediaType;
importorg.springframework.stereotype.Service;
importorg.springframework.web.client.RestTemplate;
/**
*登录接口实现
*
*@Author:heyuhua
*@Date:2021/1/817:56
*/
@Service
publicclassLoginServiceImplimplementsLoginService{
privatestaticfinalLoggerLOG=LoggerFactory.getLogger(LoginServiceImpl.class);
/**
*rest接口请求模板
*/
privatestaticRestTemplaterestTemplate=newRestTemplate();
@Override
publicLoginResultlogin(Stringaccount,Stringpassword,StringcallbackUrl){
LoginResultloginResult=null;
try{
HttpHeadersheaders=newHttpHeaders();
//设置请求媒体数据类型
headers.setContentType(MediaType.APPLICATION_JSON);
//设置返回媒体数据类型
headers.add("Accept",MediaType.APPLICATION_JSON.toString());
HttpEntityformEntity=newHttpEntity(JSON.toJSONString(newLoginUser(account,password)),headers);
loginResult=restTemplate.postForObject(callbackUrl,formEntity,LoginResult.class);
}catch(Exceptione){
LOG.error("loginvalidcallbackerror",e);
returnnewLoginResult(LoginStatus.CALLBACK_ERROR);
}
returnloginResult==null?newLoginResult(LoginStatus.ERROR):loginResult;
}
}
登录用户对象:
packagecom.hyh.sso.po;
/**
*登录用户对象
*
*@Author:heyuhua
*@Date:2021/1/816:58
*/
publicclassLoginUser{
/**
*账号
*/
privateStringaccount;
/**
*密码
*/
privateStringpassword;
/**
*登录时间
*/
privateStringloginTime;
publicLoginUser(Stringaccount,Stringpassword){
this.account=account;
this.password=password;
}
publicLoginUser(){
}
publicStringgetAccount(){
returnaccount;
}
publicvoidsetAccount(Stringaccount){
this.account=account;
}
publicStringgetPassword(){
returnpassword;
}
publicvoidsetPassword(Stringpassword){
this.password=password;
}
publicStringgetLoginTime(){
returnloginTime;
}
publicvoidsetLoginTime(StringloginTime){
this.loginTime=loginTime;
}
}
用户Token对象:
packagecom.hyh.sso.po;
importcom.hyh.utils.code.MD5;
importcom.hyh.utils.common.StringUtils;
importjava.util.Calendar;
/**
*用户Token对象
*
*@Author:heyuhua
*@Date:2021/1/817:07
*/
publicclassUserToken{
/**
*token
*/
privateStringtoken;
/**
*过期时间
*/
privateStringexpireTime;
publicUserToken(Stringtoken,StringexpireTime){
this.token=token;
this.expireTime=expireTime;
}
publicUserToken(){
}
publicstaticUserTokengetUserToken(){
CalendarnowTime=Calendar.getInstance();
nowTime.add(Calendar.MINUTE,30);
returnnewUserToken(MD5.getMD5String(StringUtils.ranStr(32)),String.valueOf(nowTime.getTimeInMillis()));
}
publicStringgetToken(){
returntoken;
}
publicvoidsetToken(Stringtoken){
this.token=token;
}
publicStringgetExpireTime(){
returnexpireTime;
}
publicvoidsetExpireTime(StringexpireTime){
this.expireTime=expireTime;
}
/**
*生成Token
*/
privateStringgenerateToken(){
returnMD5.getMD5String(StringUtils.ranStr(32));
}
}
登录结果对象:
packagecom.hyh.sso.po;
importcom.hyh.sso.LoginStatus;
importcom.hyh.sso.LoginTypes;
/**
*登录结果对象
*@Author:heyuhua
*@Date:2021/1/816:58
*/
publicclassLoginResult{
/**
*登录用户对象
*/
privateLoginUserloginUser;
/**
*登录用户令牌
*/
privateUserTokenuserToken;
/**
*登录状态
*/
privateLoginStatusloginStatus;
/**
*登录类型
*/
privateLoginTypesloginTypes;
publicLoginResult(){}
publicLoginResult(LoginStatusloginStatus){
this.loginStatus=loginStatus;
}
publicLoginUsergetLoginUser(){
returnloginUser;
}
publicvoidsetLoginUser(LoginUserloginUser){
this.loginUser=loginUser;
}
publicUserTokengetUserToken(){
returnuserToken;
}
publicvoidsetUserToken(UserTokenuserToken){
this.userToken=userToken;
}
publicLoginStatusgetLoginStatus(){
returnloginStatus;
}
publicvoidsetLoginStatus(LoginStatusloginStatus){
this.loginStatus=loginStatus;
}
publicLoginTypesgetLoginTypes(){
returnloginTypes;
}
publicvoidsetLoginTypes(LoginTypesloginTypes){
this.loginTypes=loginTypes;
}
@Override
publicStringtoString(){
return"LoginResult{"+
"loginUser="+loginUser+
",userToken="+userToken+
",loginStatus="+loginStatus+
",loginTypes="+loginTypes+
'}';
}
}
登录助手:
packagecom.hyh.sso.helper;
importcom.alibaba.fastjson.JSON;
importcom.hyh.redis.helper.RedisHelper;
importcom.hyh.sso.LoginStatus;
importcom.hyh.sso.po.LoginResult;
importcom.hyh.sso.po.LoginUser;
importcom.hyh.sso.po.UserToken;
importcom.hyh.sso.service.LoginService;
importcom.hyh.utils.common.StringUtils;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importorg.springframework.util.Assert;
importjavax.annotation.Resource;
importjava.util.Date;
importjava.util.concurrent.TimeUnit;
/**
*登录助手
*
*@Author:heyuhua
*@Date:2021/1/817:13
*/
@Component
publicclassLoginHelper{
/**
*日志
*/
privatestaticfinalLoggerLOG=LoggerFactory.getLogger(LoginHelper.class);
/**
*登录用户信息KEY
*/
privatefinalStringLOGIN_USER_KEY="login:user:";
/**
*登录用户TOKENKEY
*/
privatefinalStringLOGIN_TOKEN_KEY="login:token:";
/**
*登录失败统计KEY
*/
privatefinalStringLOGIN_FAIL_COUNT_KEY="login:fail:count";
/**
*登录失败最多允许次数
*/
privatefinallongMAX_FAIL_COUNT=5;
/**
*登录服务
*/
@Resource
privateLoginServiceloginService;
/**
*redis助手
*/
@Autowired
privateRedisHelperredisHelper;
/**
*登录
*
*@paramaccount用户名
*@parampassword密码
*@paramcallbackUrl回调URL
*@return
*/
publicLoginResultlogin(Stringaccount,Stringpassword,StringcallbackUrl){
Assert.notNull(account,"accountisnull");
Assert.notNull(password,"passwordisnull");
Assert.notNull(callbackUrl,"callbackUrlisnull");
//判断账户是否多次登录失败被锁定
Stringvalue=redisHelper.getStringValue(LOGIN_FAIL_COUNT_KEY+account);
if(StringUtils.isNotBlank(value)){
LongloginFailCount=Long.parseLong(value);
if(loginFailCount.longValue()>=MAX_FAIL_COUNT){
returnnewLoginResult(LoginStatus.ACCOUNT_LOCK);
}
}
//登录操作
LoginResultloginResult=loginService.login(account,password,callbackUrl);
switch(loginResult.getLoginStatus()){
caseSUCCESS:
//登录成功
loginSuccess(loginResult);
break;
caseFAIL:
//登录失败
loginFail(loginResult);
break;
caseERROR:
loginError(loginResult);
//登录异常
break;
default:
break;
}
returnloginResult;
}
/**
*注销
*
*@paramaccount
*@paramtoken
*/
publicvoidlogout(Stringaccount,Stringtoken){
Assert.notNull(account,"accountisnull");
Assert.notNull(token,"tokenisnull");
removeKey(account,token);
}
/**
*注销
*
*@paramtoken
*/
publicvoidlogout(Stringtoken){
Assert.notNull(token,"tokenisnull");
removeKey(token);
}
/**
*获取登录用户
*
*@paramtoken
*@return
*/
publicLoginUsergetLoginUser(Stringtoken){
Assert.notNull(token,"tokenisnull");
Stringvalue=redisHelper.getStringValue(LOGIN_USER_KEY+token);
if(StringUtils.isNotBlank(value)){
returnJSON.parseObject(value,LoginUser.class);
}
returnnull;
}
/**
*移除key
*
*@paramaccount
*@paramtoken
*/
privatevoidremoveKey(Stringaccount,Stringtoken){
redisHelper.del(LOGIN_FAIL_COUNT_KEY+account);
redisHelper.del(LOGIN_TOKEN_KEY+account);
redisHelper.del(LOGIN_USER_KEY+token);
}
/**
*移除Key
*
*@paramtoken
*/
privatevoidremoveKey(Stringtoken){
redisHelper.del(LOGIN_USER_KEY+token);
//其余的key到达过期时间自动过期
}
/**
*登录异常
*
*@paramloginResult
*/
privatevoidloginError(LoginResultloginResult){
LOG.error("user【"+loginResult.getLoginUser().getAccount()+"】loginerror");
}
/**
*登录失败操作
*
*@paramloginResult
*/
privatevoidloginFail(LoginResultloginResult){
Stringkey=LOGIN_FAIL_COUNT_KEY+loginResult.getLoginUser();
redisHelper.increment(key,30*60*1000);
}
/**
*登录成功操作
*
*@paramloginResult
*/
privatevoidloginSuccess(LoginResultloginResult){
LoginUserloginUser=loginResult.getLoginUser();
loginUser.setLoginTime(String.valueOf(newDate().getTime()));
UserTokenuserToken=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:8088 spring: #redis配置 redis: host:192.168.6.134 port:30511 password:
4.单元测试
测试代码如下(示例):
@Autowired
privateLoginHelperloginHelper;
@Test
publicvoidtestLogin(){
//测试时先开启HyhBootApplication
Stringaccount="hyh";
Stringpassword="hyh-pwd";
StringcllbackUrl="http://localhost:8088/hyh/login";//在com.hyh.core.web下可查看
LoginResultloginResult=loginHelper.login(account,password,cllbackUrl);
System.out.println("loginResult:"+loginResult.toString());
}
//控制层代码
@RequestMapping(value="login",method=RequestMethod.POST)
publicLoginResultlogin(@RequestBodyLoginUserloginUser){
Assert.notNull(loginUser.getAccount(),"accountisnull");
Assert.notNull(loginUser.getPassword(),"passwordisnull");
LoginResultloginResult=newLoginResult(LoginStatus.SUCCESS);
loginResult.setLoginUser(loginUser);
//模拟直接返回登录成功
returnloginResult;
}
总结
是不是感觉很简单?更多用法请点击下方查看源码,关注我带你揭秘更多高级用法
源码地址:点此查看源码.
到此这篇关于SpringBootSSO轻松实现(附demo)的文章就介绍到这了,更多相关SpringBootSSO内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!