SpringBoot整合SSO(single sign on)单点登录

1、单点登录三种常见的方式

(1)Session广播机制(Session复制)
(2)使用Cookie+Redis实现
(3)使用token实现

2、单点登录介绍

举例:

(1)引入jwt依赖

 <!-- JWT-->
<dependency>
 <groupId>io.jsonwebtoken</groupId>
 <artifactId>jjwt</artifactId>
</dependency>

(2)创建JWTUtils工具类

public class JwtUtils {

 //token过期时间
 public static final long EXPIRE = 1000 * 60 * 60 * 24;
 //秘钥
 public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";

 public static String getJwtToken(String id, String nickname){

  String JwtToken = Jwts.builder()
    //设置头信息
    .setHeaderParam("typ", "JWT")
    .setHeaderParam("alg", "HS256")
    .setSubject("user")
    .setIssuedAt(new Date())
    //设置过期时间
    .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
    //设置token主体部分(这里使用id和nickname作为主体部分)
    .claim("id", id)
    .claim("nickname", nickname)
    //加密方式
    .signWith(SignatureAlgorithm.HS256, APP_SECRET)
    .compact();

  return JwtToken;
 }

 /**
  * 判断token是否存在与有效(直接通过APP_SECRET解析token)
  * @param jwtToken
  * @return
  */
 public static boolean checkToken(String jwtToken) {
  if(StringUtils.isEmpty(jwtToken)) return false;
  try {
   Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
  } catch (Exception e) {
   e.printStackTrace();
   return false;
  }
  return true;
 }

 /**
  * 判断token是否存在与有效(通过获取请求头信息获取token再使用APP_SECRET解析token)
  * @param request
  * @return
  */
 public static boolean checkToken(HttpServletRequest request) {
  try {
   String jwtToken = request.getHeader("token");
   if(StringUtils.isEmpty(jwtToken)) return false;
   Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
  } catch (Exception e) {
   e.printStackTrace();
   return false;
  }
  return true;
 }

 /**
  * 根据token字符串获取用户id(取出有效载荷中的用户信息)
  * @param request
  * @return
  */
 public static String getMemberIdByJwtToken(HttpServletRequest request) {
  String jwtToken = request.getHeader("token");
  if(StringUtils.isEmpty(jwtToken)) return "";
  Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
  Claims claims = claimsJws.getBody();
  return (String)claims.get("id");
 }
}

3、单点登录实现

项目目录结构

UcenterMemberController

@RestController
@RequestMapping("/user/")
@CrossOrigin
public class UcenterMemberController {

 @Autowired
 private UcenterMemberService ucenterMemberService;
	//登录
 @PostMapping("login")
 public ResponseResult login(@RequestBody MobileLoginRequest request) {
  String token = ucenterMemberService.login(request);
  return ResponseResult.success().data("token", token);
 }
	//注册
 @PostMapping("register")
 public ResponseResult register(@RequestBody RegisterRequest request) {
  ucenterMemberService.register(request);
  return ResponseResult.success().message("注册成功");
 }
	//根据token获取用户信息
 @GetMapping("getUserInfo")
 public ResponseResult getUserInfo(HttpServletRequest request) {
  //调用jwt工具类的方法,根据request对象获取头信息,返回用户id
  String id = JwtUtils.getMemberIdByJwtToken(request);
  //根据用户id查询用户
  UcenterMember member = ucenterMemberService.getById(id);
  return ResponseResult.success().data("userInfo", member);
 }

}

ServiceImpl

@Service
public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService {

 @Autowired
 private StringRedisTemplate redisTemplate;

	//登录
 @Override
 public String login(MobileLoginRequest request) {

  String phone = request.getPhone();
  String password = request.getPassword();
  if (StrUtil.isBlank(phone) || StrUtil.isBlank(password)) {
   throw new GuliException(200001, "请输入用户名或者密码");
  }
  //根据输入的手机号码查找该用户信息
  UcenterMember ucenterByPhone = this.baseMapper.selectOne(new LambdaQueryWrapper<UcenterMember>().eq(UcenterMember::getMobile, phone));
  if (ucenterByPhone == null) {
   throw new GuliException(200002, "该用户名不存在");
  }
  //如果用户存在比对数据库密码和用户输入的密码
  if (!MD5Util.encrypt(password).equals(ucenterByPhone.getPassword())) {
   throw new GuliException(200003, "密码输入错误");
  }
  String token = JwtUtils.getJwtToken(ucenterByPhone.getId(), ucenterByPhone.getNickname());
  return token;
 }

	//注册
 @Override
 public void register(RegisterRequest request) {
  String phone = request.getPhone();
  String password = request.getPassword();
  String nickName = request.getNickName();
  String code = request.getCode();
  if (StrUtil.isBlank(phone) || StrUtil.isBlank(password) || StrUtil.isBlank(nickName) || StrUtil.isBlank(code)) {
   throw new GuliException(200001, "请填写相关信息");
  }
  //判断手机号是否重复
  Integer count = baseMapper.selectCount(new LambdaQueryWrapper<UcenterMember>().eq(UcenterMember::getMobile, phone));
  if (count > 0) {
   throw new GuliException(200001, "账号已经存在请重新输入");
  }
  //验证code
  String redisCode = redisTemplate.opsForValue().get(phone);
  if (StrUtil.isBlank(redisCode)) {
   throw new GuliException(200001, "验证码已经过期,请重新获取");
  }
  if (!redisCode.equals(code)) {
   throw new GuliException(200001, "验证码错误");
  }

  UcenterMember ucenterByPhone = new UcenterMember();
  ucenterByPhone.setMobile(phone);
  ucenterByPhone.setPassword(MD5Util.encrypt(password));
  ucenterByPhone.setNickname(nickName);
  ucenterByPhone.setIsDisabled(false);
  int insert = baseMapper.insert(ucenterByPhone);
  if(insert<=0){
   throw new GuliException(20001,"注册失败");
  }
 }
}

MD5加密算法工具类

public final class MD5Util {

 public static String encrypt(String strSrc) {
  try {
   char hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
     '9', 'a', 'b', 'c', 'd', 'e', 'f'};
   byte[] bytes = strSrc.getBytes();
   MessageDigest md = MessageDigest.getInstance("MD5");
   md.update(bytes);
   bytes = md.digest();
   int j = bytes.length;
   char[] chars = new char[j * 2];
   int k = 0;
   for (int i = 0; i < bytes.length; i++) {
    byte b = bytes[i];
    chars[k++] = hexChars[b >>> 4 & 0xf];
    chars[k++] = hexChars[b & 0xf];
   }
   return new String(chars);
  } catch (NoSuchAlgorithmException e) {
   e.printStackTrace();
   throw new RuntimeException("MD5加密出错!!+" + e);
  }
 }

 public static void main(String[] args) {
  System.out.println(MD5Util.encrypt("111111"));
 }

}

4、登录完成后在前端界面展示用户信息

(1)第一、二、四步:登录的方法(记得npm install js-cookie)

 //登录的方法
  submitLogin() {
  //第一步 调用接口进行登录,返回token字符串
  loginApi.submitLoginUser(this.user)
   .then(response => {
    //第二步 获取token字符串放到cookie里面
    //第一个参数cookie名称,第二个参数值,第三个参数作用范围
    cookie.set('user_token',response.data.data.token,{domain: 'localhost'})

    //第四步 调用接口 根据token获取用户信息,为了首页面显示
    loginApi.getLoginUserInfo()
    .then(response => {
     this.loginInfo = response.data.data.userInfo
     //获取返回用户信息,放到cookie里面(主页在cookie中获取用户信息进行展示)
     cookie.set('user_info',this.loginInfo,{domain: 'localhost'})

     //跳转页面
     window.location.href = "/";
    })
   })
  },

(2)第三步:在request.js中编写前端请求拦截器(发送请求携带token)

// 创建axios实例
const service = axios.create({
 baseURL: process.env.BASE_API, // api 的 base_url
 timeout: 5000 // 请求超时时间
})

// request拦截器
service.interceptors.request.use(
 config => {
 if (cookie.get('user_token')) {
  config.headers['token'] = cookie.get('user_token') // 让每个请求携带自定义token 请根据实际情况自行修改
 }
 return config
 },
 error => {
 // Do something with request error
 console.log(error) // for debug
 Promise.reject(error)
 }
)

(3)第五步:主页显示用户信息(从cookie中获取用户信息)

//创建方法,从cookie获取用户信息
showInfo() {
 //从cookie获取用户信息
 var userStr = cookie.get('guli_ucenter')
 // 把字符串转换json对象(js对象),因为后端传过来的是"{'name','lucy','age':18}"的格式
 if(userStr) {
 this.loginInfo = JSON.parse(userStr)
 }
}

显示用户信息(根据userInfo中id来判断)

<ul class="h-r-login">
 //cookie中没有用户信息,显示登录和注册
 <li v-if="!loginInfo.id" id="no-login">
  <a href="/login" rel="external nofollow" title="登录">
   <em class="icon18 login-icon">&nbsp;</em>
   <span class="vam ml5">登录</span>
  </a>
  |
  <a href="/register" rel="external nofollow" title="注册">
   <span class="vam ml5">注册</span>
  </a>
 </li>
 //cookie中有用户信息,显示用户头像、昵称和退出
 <li v-if="loginInfo.id" id="is-login-two" class="h-r-user">
  <a href="/ucenter" rel="external nofollow" title>
   <img
     :src="loginInfo.avatar"
     width="30"
     height="30"
     class="vam picImg"
     alt
     >
   <span id="userName" class="vam disIb">{{ loginInfo.nickname }}</span>
  </a>
  <a href="javascript:void(0);" rel="external nofollow" title="退出" @click="logout()" class="ml5">退出</a>
 </li>
</ul>

退出登录,清空cookie中的token和用户信息

 //退出
 logout() {
  //清空cookie值
  cookie.set('user_token','',{domain: 'localhost'})
  cookie.set('user_info','',{domain: 'localhost'})
  //回到首页面
  window.location.href = "/";
 }
 }

到此这篇关于SpringBoot整合SSO(single sign on)单点登录的文章就介绍到这了,更多相关SpringBoot整合SSO单点登录内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用springboot结合vue实现sso单点登录

    本文实例为大家分享了springboot vue实现sso单点登录的具体代码,供大家参考,具体内容如下 项目结构: 开发工具:idea, maven3 静态文件下载地址 1.pom文件: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.or

  • SpringBoot整合SSO(single sign on)单点登录

    1.单点登录三种常见的方式 (1)Session广播机制(Session复制) (2)使用Cookie+Redis实现 (3)使用token实现 2.单点登录介绍 举例: (1)引入jwt依赖 <!-- JWT--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> (2)创建JWTUtil

  • 基于SpringBoot+Redis的Session共享与单点登录详解

    前言 使用Redis来实现Session共享,其实网上已经有很多例子了,这是确保在集群部署中最典型的redis使用场景.在SpringBoot项目中,其实可以一行运行代码都不用写,只需要简单添加添加依赖和一行注解就可以实现(当然配置信息还是需要的). 然后简单地把该项目部署到不同的tomcat下,比如不同的端口(A.B),但项目访问路径是相同的.此时在A中使用set方法,然后在B中使用get方法,就可以发现B中可以获取A中设置的内容. 但如果就把这样的一个项目在多个tomcat中的部署说实现了单

  • GoLang jwt无感刷新与SSO单点登录限制解除方法详解

    目录 前言 为什么使用JWT Cookie和Session token (header.payload.signature) token 安全性 基于token安全性的处理 客户端与服务端基于无感刷新流程图 golang实现atoken和rtoken 颁发token 校验token 无感刷新token 完整实现代码 SSO(Single Sign On)单用户登录以及无感刷新token 实现思路 实战代码 小结 前言 为什么使用JWT Jwt提供了生成token以及token验证的方法,而tok

  • springboot整合security和vue的实践

    目录 环境 1.security参考资料 认证流程原理: 2.springboot整合security要点 2.1获取登录用户信息 2.2自定义登入登出url 2.3自定义Handler返回json 2.4记住我功能 2.5验证码功能 2.6限制登录次数 2.7密码加密 2.8后台提供接口,返回前端json,整合vue做前端登入登出 3.测试 环境 springboot1.5.9 完整代码,内有sql,先建库,在运行sql建表,sql中已插入测试的数据. https://github.com/2

  • springboot 集成cas5.3 实现sso单点登录详细流程

    什么是单点登录? 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. 我们目前的系统存在诸多子系统,而这些子系统是分别部署在不同的服务器中,那么使用传统方式的session是无法解决的,我们需要使用相关的单点登录技术来解决. SSO单点登录访问流程主要有以下步骤: 访问服务:SSO客户端发送请求访问应用系统提供的服务资源. 定向认证:SSO客户端会重定向用户请求

  • SpringBoot如何实现同域SSO(单点登录)

    单点登录,其实看起来不是很复杂,只是细节上的处理,单点区分有三种 同域SSO 同父域SSO 跨域的SSO 如何实现同域SSO? 个人理解:当用户登录访问demo1.lzmvlog.top时,同时具有访问demo2.lzmvlog.top的能力,即认证完成一次,可以访问所有系统. 实现方式:可以采用Cookie实现,即用户在访问一个系统时,携带认证颁发的信息,系统响应是否具有访问资格,否则跳转认证,也可以采用Session,即Session共享,校验访问用户是否具有有效的信息,提供访问资格 代码实

  • SpringBoot整合Keycloak实现单点登录的示例代码

    目录 1. 搭建Keycloak服务器 2. 配置权限 2.1. 登陆 2.2. 创建Realm 2.3. 创建用户 2.4. 创建客户端 2.5. 创建角色 2.6. 配置用户角色关系 2.7. 配置客户端和角色关系 3. 整合SpringBoot 3.1. 引入核心依赖 3.2. 编写Controller 3.3. 编写application.yml 4. 验证 Keycloak是一个开源的身份和权限访问管理工具,轻松为应用程序和安全服务添加身份验证,无需处理储存用户或者验证用户,其提供用户

  • springboot集成CAS实现单点登录的示例代码

    最近新参与的项目用到了cas单点登录,我还不会,这怎么能容忍!空了学习并搭建了一个spring-boot 集成CAS 的demo.实现了单点登录与登出. 单点登录英文全称是:Single Sign On,简称SSO. 含义:在多个相互信任的系统中,只要登录一个系统其他系统均可访问. CAS 是一种使用广泛的单点登录实现,分为客户端CAS Client和服务端 CAS Service,客户端就是我们的系统,服务端是认证中心,由CAS提供,我们需要稍作修改,启动起来就可以用.~~~~ 效果演示 ht

  • SpringBoot集成redis与session实现分布式单点登录

    目录 单点登录 SSO(Single Sign On) 什么是单点登录? 实现方式 开发技术 单点登录实现流程 实现案例 看效果 前言: 由于考虑到cookie的安全性问题,就有了下面这个版本的sso 单点登录 SSO(Single Sign On) 什么是单点登录? 单点登录的英文名叫做:Single Sign On(简称SSO),指在同一帐号平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的系统.简而言之,多个系统,统一登陆. 我们可以这样理解,在一个服务模块登录后,其他模块无

随机推荐