SpringBoot整合SSO(single sign on)單點登入
阿新 • • 發佈:2020-06-19
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"> </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單點登入內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!