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

 更新时间:2020年06月17日 12:05:29   作者:Mr Kwan  
这篇文章主要介绍了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单点登录内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring使用@Filter注解创建自定义过滤器

    Spring使用@Filter注解创建自定义过滤器

    Spring 中鲜为人知但非常有用的注解之一是 @Filter,它支持自定义过滤器,下面我们就来深入研究一下如何使用 Spring 的 @Filter 注解来创建自定义过滤器吧
    2023-11-11
  • SpringBoot不读取bootstrap.yml/properties文件问题

    SpringBoot不读取bootstrap.yml/properties文件问题

    这篇文章主要介绍了SpringBoot不读取bootstrap.yml/properties文件问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java中ArrayList和LinkedList的区别

    Java中ArrayList和LinkedList的区别

    ArrayList和LinkedList在这个方法上存在一定的性能差异,本文就介绍了Java中ArrayList和LinkedList的区别,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • Spring Cloud Gateway全局通用异常处理的实现

    Spring Cloud Gateway全局通用异常处理的实现

    这篇文章主要介绍了Spring Cloud Gateway全局通用异常处理的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • java使用WatchService监控文件夹示例

    java使用WatchService监控文件夹示例

    本篇文章主要介绍了java使用WatchService监控文件夹示例的资料,这里整理了详细的代码,有需要的小伙伴可以参考下。
    2017-02-02
  • Lombok的详细使用及优缺点总结

    Lombok的详细使用及优缺点总结

    最近在学Mybatis,接触到了Lombok的使用,所以写一篇文章记录一下,包括lombok的安装及使用优缺点,感兴趣的朋友跟随小编一起看看吧
    2021-07-07
  • 一文搞懂java中类及static关键字执行顺序

    一文搞懂java中类及static关键字执行顺序

    这篇文章主要介绍了一文搞懂java中类及static关键字执行顺序,文章通过类的生命周期展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09
  • Java之BigDecimal实现详解

    Java之BigDecimal实现详解

    这篇文章主要介绍了Java之BigDecimal实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • MyBatis参数处理实现方法汇总

    MyBatis参数处理实现方法汇总

    这篇文章主要介绍了MyBatis参数处理实现方法汇总,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • 使用SpringBoot+OkHttp+fastjson实现Github的OAuth第三方登录

    使用SpringBoot+OkHttp+fastjson实现Github的OAuth第三方登录

    这篇文章主要介绍了使用SpringBoot+OkHttp+fastjson实现Github的OAuth第三方登录,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02

最新评论