单点登录概述:
多系统共存下,用户在一处地方登录,得到其他所有系统的信任,无需再次登录。
自己的理解:在前端用户点击登录触发后端登录接口,登录成功的时候,后端jwt生成一个token,后端将token返给前端,前端把token放进需要进行验证的接口的请求头(header)中去,用户带着这个token去进行验证。(token设置过期时间),验证成功则拦截器放行,否则拦截
这里介绍一些jwt基本概念
JWT:
1.认证流程
a.首先,前端通过web表单将自己的用户民和密码发送到后端的接口,这一过程一般是一个HTTP,POST请求。建议的方式是通过SSL
加密的方式传输(https协议),从而避免敏感信息被嗅探。
b.后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成
一个JWT(token),形成的JWT就是一个形同111.zzz.xxx的字符串, token,head ,payload, singurater
c.后端将JWT字符串 作为登录成功的返回结果返回给前端,前端可以返回的结果保存在localStorage 或sessionStorage上,退出登录
时前端删除保存的JWT即可。
d.前端在每次请求时将JWT放入HTTP Header中的Authoriztion 位。(放在Header中的原因是解决XSS和XSRF问题) HEADER.
e.后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)
f。验证通过后,后端使用JWT包含的用户信息进行其他逻辑操作,返回相应结果。
2.JWT优势
可以通过URL,POST参数或者在HTTP,header发送 ,因为数据量小,传输速度也很快。
自包含(self-contained): 负载中包含了所有用户所需要的信息,避免了多次查询数据库。
因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
不需要在服务端保存会话信息,特别适用于分布式微服务。
3.JWT结构
a.令牌组成
token 其实就相当于一段字符串 ==> token String ==>header.payload.singnature =token
1.标头(Header)
2.有效载荷(Payload)
3.签名(Signature)
因此,JWT通常如下所示:xxxx.yyyy.zzzz
4.JWT常见异常信息
SignatureVerificationException 签名不一致
TokenExpiredException 令牌过期异常
AlgorithmMismatchException 算法不匹配异常(也就是解密与加密的算法不一样)
InvalidClaimExeption 失效的Payload异常 (就是Payload加解密前后信息不一致)
总的来说就是登录的时候使用jwt生成一个token,用户带着这个token去其他页面进行验证,需要配合拦截器来实现。
作者有自己写的单点登录的demo源码如有需要可随时滴滴~
使用原理(新手切记):
登录成功生成token后,需要将token返回前端也就是代码中的map.put("token",token),前端需要把这个token加入到请求头中去,然后后端进行接口验证,切记拦截器建议拦截所有路径,放行登录接口(如拦截无法进行登录验证)。
使用postman测试时,先调用登录接口生成token,然后调用其他接口时在Header里面设置content-Type,application, token=生成的token值,就可以实现token验证了。
直接复制即可测试使用,
首先要准备好一个util包
JWTutils.java
public class JWTutils { private static final String SING="!@#$%^&"; /* * 生成token * */ public static String getToken (Mapmap){ Calendar instance=Calendar.getInstance(); instance.add(Calendar.MINUTE,15);//15分钟 //创建JWT builder JWTCreator.Builder builder= JWT.create(); map.forEach((k,v)->{ builder.withClaim(k,v); }); String token=builder.withExpiresAt(instance.getTime())//指定令牌过期时间 .sign(Algorithm.HMAC256(SING)); //sign return token; } /* * 验证token 合法性 * */ // //1.创建验证对象(采用加密时的算法) // JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256("!@#$%^^&")).build(); // //2.解密(报这个错说明是token时间过期了)(com.auth0.jwt.exceptions. // // TokenExpiredException: The Token has expired on Sat May 14 21:28:41 CST 2022.) // DecodedJWT verify=jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTI1MzU5ODEsInVzZXJJZCI6MjEsInVzZXJuYW1lIjoieGlhb2NoZW4ifQ.YGaiUwCOCmAyq0RUAA-61Et3TfTb61pZoTjgDbXiFJg"); public static DecodedJWT verify(String token){ return JWT.require(Algorithm.HMAC256(SING)).build().verify(token); } /* * 获取token信息方法 (该方法可以省略),可以直接在验证token方法中 renturn 返回 * */ public static DecodedJWT getTokenInfo(String token){ DecodedJWT verify=JWT.require(Algorithm.HMAC256(SING)).build().verify(token); return verify; } }
JWTinterveptor.java(这里验证token)
public class JWTinterveptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Mapmap=new HashMap<>();
//获取请求头中令牌
String token =request.getHeader("token");try {
JWTutils.verify(token); //去JWTutils中验证令牌
return true; //这一步到拦截器中去进行验证
}catch (SignatureVerificationException e){
e.printStackTrace();
map.put("msg","无效签名");
} catch (TokenExpiredException e){
e.printStackTrace();
map.put("msg","token过期");
}catch (AlgorithmMismatchException e){
map.put("msg","算法不一致");
}catch (Exception e){
map.put("msg","token无效");
}
map.put("state",false);//设置状态//将map转为json
String json=new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
InterceptorConfig.java(这里进行验证)
@Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JWTinterveptor()) .addPathPatterns("/**")//拦截 (其他接口token验证) .excludePathPatterns("/User/UserLogin");//所有用户都放行(登录接口不放行,无法进行登录) } }
UserController.java(控制层)
package com.zufeng.lyh.controller;
import com.zufeng.lyh.domain.User;
import com.zufeng.lyh.service.UserService;
import com.zufeng.lyh.utils.JWTutils;
import jdk.nashorn.internal.parser.Token;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;@GetMapping("/login")
public Maplogin(User user){
Mapmap=new HashMap<>(); try {
User userDB= userService.login(user);
Mappayload=new HashMap<>();
payload.put("id",userDB.getId());
payload.put("name",userDB.getName());
//生成JWT的令牌
String token=JWTutils.getToken(payload);
JWTutils.getToken(payload);map.put("state",true);
map.put("msg","认证成功");
map.put("token",token);//传送token
}catch (Exception e){
e.printStackTrace();
map.put("state",false);
}
return map;
}@PostMapping("/test")
public Maptest(HttpServletRequest request){
Mapmap=new HashMap<>();
//处理自己的业务逻辑 map.put("state",true); map.put("msg","请求成功"); return map; }}
over!!!