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

作者:Mr Kwan 时间:2022-02-02 03:39:40 

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

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

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

2、单点登录介绍

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

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

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

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

举例:

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

(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、单点登录实现

项目目录结构

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

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、登录完成后在前端界面展示用户信息

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

(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 = "/";
}
}

来源:https://blog.csdn.net/qq_27462223/article/details/106763419

标签:SpringBoot,SSO,单点登录
0
投稿

猜你喜欢

  • Flutter中http请求抓包的完美解决方案

    2023-08-22 18:47:47
  • 基于ThreadLocal 的用法及内存泄露(内存溢出)

    2023-11-23 20:22:28
  • Java 反射机制知识详细介绍及总结

    2023-12-15 01:08:15
  • 一文了解Spring中拦截器的原理与使用

    2023-06-30 13:28:05
  • Java源码解析之平衡二叉树

    2023-11-29 11:16:40
  • Java webservice的POST和GET请求调用方式

    2023-01-10 05:35:43
  • Spring Boot 配置和使用多线程池的实现

    2022-09-04 19:53:02
  • SpringBoot中定时任务@Scheduled注解的使用解读

    2022-11-24 17:20:11
  • android中使用SharedPreferences进行数据存储的操作方法

    2023-06-16 17:37:42
  • mybatis源码解读之executor包懒加载功能 

    2022-09-17 00:28:05
  • Spring Boot常用注解(经典干货)

    2023-11-24 22:29:10
  • 使用logback屏蔽一些包的日志

    2023-08-08 20:46:20
  • Java算法比赛常用方法实例总结

    2023-11-28 07:15:26
  • springboot集成mybatisPlus+多数据源的实现示例

    2023-11-24 22:35:02
  • Java实现多用户注册登录的幸运抽奖

    2023-07-30 11:57:44
  • Android带进度的圆形进度条

    2023-08-05 21:47:18
  • java中用String.Join美化代码的实例讲解

    2022-03-04 08:17:04
  • SpringBoot整合之SpringBoot整合MongoDB的详细步骤

    2023-11-25 09:55:37
  • Jenkins Pipeline 部署 SpringBoot 应用的教程详解

    2022-09-26 14:06:27
  • Java中PriorityQueue实现最小堆和最大堆的用法

    2022-03-25 14:32:18
  • asp之家 软件编程 m.aspxhome.com