1.什么是SSO
SSO的全称是 Single Sign On 单点登录。就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。
直白来说SSO就是来检查用户的登录状态。
例如在腾讯首页登录后,跳转QQ音乐,腾讯会议等站点都不需要再次登录。
2.SSO项目
以xxl-sso项目(有修改)举例说明SSO:
核心代码:包含登录,登出,登录状态检查,注册 四个功能
@RestController
@RequestMapping("/app")
public class AppController {
@Autowired
private UserService userService;
/**
* Login
*
* @param username
* @param password
* @return
*/
@RequestMapping("/login")
@ResponseBody
public ReturnT<String> login(String username, String password) {
// valid login
ReturnT<UserInfo> result = userService.findUser(username, password);
if (result.getCode() != ReturnT.SUCCESS_CODE) {
return new ReturnT<String>(result.getCode(), result.getMsg());
}
// 1、make xxl-sso user
XxlSsoUser xxlUser = new XxlSsoUser();
xxlUser.setUserid(String.valueOf(result.getData().getId()));
xxlUser.setUsername(result.getData().getUserName());
xxlUser.setVersion(UUID.randomUUID().toString().replaceAll("-", ""));
xxlUser.setExpireMinute(SsoLoginStore.getRedisExpireMinute());
xxlUser.setExpireFreshTime(System.currentTimeMillis());
// 2、generate sessionId + storeKey
String sessionId = SsoSessionIdHelper.makeSessionId(xxlUser);
// 3、login, store storeKey
SsoTokenLoginHelper.login(sessionId, xxlUser);
// 4、return sessionId
return new ReturnT<String>(sessionId);
}
/**
* Logout
*
* @param sessionId
* @return
*/
@RequestMapping("/logout")
@ResponseBody
public ReturnT<String> logout(String sessionId) {
// logout, remove storeKey
SsoTokenLoginHelper.logout(sessionId);
return ReturnT.SUCCESS;
}
/**
* logincheck
*
* @param token
* @return
*/
@RequestMapping("/logincheck")
@ResponseBody
public ReturnT<XxlSsoUser> logincheck(String token) {
XxlSsoUser xxlUser = SsoTokenLoginHelper.loginCheck(token);
if (xxlUser == null) {
return new ReturnT<XxlSsoUser>(ReturnT.FAIL_CODE, "sso not login.");
}
return new ReturnT<XxlSsoUser>(xxlUser);
}
@RequestMapping("/sign")
@ResponseBody
public ReturnT<Boolean> sign(@Validated @RequestBody UserSignParam userSignParam){
UserInfo userInfo = new UserInfo();
BeanUtils.copyProperties(userSignParam,userInfo);
return userService.signUser(userInfo);
}
}
SSO只提供这四个接口,与业务逻辑无关。
关于配置文件:
server:
port: 8089
servlet:
context-path: /hongyan-sso
spring:
application:
name: qcbysso
profiles:
active: local
freemarker:
templateLoaderPath: classpath:/templates/
suffix: .ftl
charset: UTF-8
request-context-attribute: request
settings.number_format: 0.##########
使用spring.profiles.active = local来决定使用local配置文件
使用server.context-path = /hongyan-sso 是使接口都添加一个一级路径
接口的三级路径:
第一路径是项目路径 context-path
第二路径是模块路径
第三路径是具体功能
3.相关
设计思想
方式01: Redis单节点 + Jedis单例 : Redis单节点压力过重, Jedis单例存在并发瓶颈 》》不可用于线上
new Jedis("127.0.0.1", 6379).get("cache_key");
方式02: Redis单节点 + JedisPool单节点连接池 》》 Redis单节点压力过重,负载和容灾比较差
new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6379, 10000).getResource().get("cache_key");
方式03: Redis分片(通过client端集群,一致性哈希方式实现) + Jedis多节点连接池 》》Redis集群,负载和容灾较好, ShardedJedisPool一致性哈希分片,读写均匀,动态扩充
new ShardedJedisPool(new JedisPoolConfig(), new LinkedList<JedisShardInfo>());
方式01:redis单节点 + jedis单例使两个任何一个出现问题都会导致服务终止。
方式02:jedis使用集群,瓶颈出现在redis,导致只能用在非高并发,项目简单,数据不敏感的情况。
池化思想(牺牲空间换取时间)
例如:数据库连接池,jedis连接池,线程池等等,每次项目启动时先创建一些连接,可以直接用。因为创建连接是需要时间的(如果项目启动创建10条连接,需求只有1条,那么剩余的连接继续使用不回收)