【辰兮要努力】:hello你好我是辰兮,很高兴你能来阅读,昵称是希望自己能不断精进,向着优秀程序员前行!
博客来源于项目以及编程中遇到的问题总结,偶尔会有读书分享,我会陆续更新Java前端、后台、数据库、项目案例等相关知识点总结,感谢你的阅读和关注,希望我的博客能帮助到更多的人,分享获取新知,大家一起进步!
吾等采石之人,应怀大教堂之心,愿你们奔赴在各自的热爱中…
JWT源码分析
:JWT源码学习小结
文章目录
一、SSO概念
单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
阿里系的淘宝和天猫,很明显地我们可以知道这是两个系统,但是你在使用的时候,登录了天猫,淘宝也会自动登录。
简述:当你成功登录后,系统会返回你一个令牌(凭证),你可以拿着这个令牌去访问所有相关的系统,只要你令牌即可访问,没有令牌就被拦截不能访问。(参考下图)
1、相比于单系统登录,sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。
2、间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。
二、JWT单点登录步骤
添加依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
controller层的实现
@RequestMapping("/login")
public Map<String,String> login(String userName, String password){
Map<String,String> map=new HashMap<>();
try{
String token = Jwts.builder().setSubject(userName) //主题,可以放用户的详细信息
.setIssuedAt(new Date()) //token创建时间
.setExpiration(new Date(System.currentTimeMillis() + 60000)) //token过期时间
.setId("userId") //用户ID
//.setClaims(hashMap) //配置角色信息
.signWith(SignatureAlgorithm.HS256, "WuHan") //加密方式和加密密码
.compact();
// System.out.println("token:"+token);
map.put("code","1");
map.put("msg","success");
map.put("token",token);
map.put("user",userName);
}catch (Exception e){
map.put("code","0");
map.put("msg","fail");
e.printStackTrace();
}
return map;
}
登录后可以把token打印出来自己看看,测试如下
token:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4YyIsImlhdCI6MTU5MTE1NDQwNiwiZXhwIjoxNTkxMTU0NDY2LCJqdGkiOiJ1c2VySWQifQ.c7gCDgIQ_I40dIWRxyG4yd1xaZQyWflnC7kX2Uoc9H8
token:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4YyIsImlhdCI6MTU5MTE1OTA1MSwiZXhwIjoxNTkxMTU5MTExLCJqdGkiOiJ1c2VySWQifQ.eUp0O2y83dc14f9NPUX049E9VxMpnkjuTcrheq2r9fM
token构成包含三个部分:
- Header 头部
- Payload 负载
- Signature 签名
注意事项:
①根据需求设计过期时间(1秒 =1000 毫秒 )
②因为我把时间设计到token里面了所以你可以测试每次产生的token不一样
③设置userId那一栏-我目前是写死了 setId(“userId”) ,你可以根据需求设计不同的值进去
校验Token
try {
JwtParser parser = Jwts.parser();
parser.setSigningKey("WuHan");//解析 要和上面“暗号”一样
Jws<Claims> claimsJws = parser.parseClaimsJws(token);
Claims body = claimsJws.getBody();
String username = body.getSubject();
// Object role = body.get("role");
return true;
} catch (ExpiredJwtException e) {
e.printStackTrace();
} catch (UnsupportedJwtException e) {
e.printStackTrace();
} catch (MalformedJwtException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
setSigningKey() 与builder中签名方法signWith()对应,parser中的此方法拥有与signWith()方法相同的三种参数形式,用于设置JWT的签名key,用户后面对JWT进行解析。
isSigned() 校验JWT是否进行签名。方法很简单,以分隔符" . ",截取JWT第三段,即签名部分进行判断。
- 案例前端代码实现
登录模块如果成功登录,则存放token(也可以存放user根据需求看)
<script type="text/javascript">
$("#login").click(function () {
var name=$("#userName").val();
var pwd=$("#password").val();
$.post("http://localhost:8080/user/login",{userName:name,password:pwd},function(data) {
console.log(data);
if(data.code=="1"){
/* document.cookie=data.token;*/
sessionStorage.setItem("token",data.token);
sessionStorage.setItem("user",data.user);
window.location.href="index.html";
}else{
window.location.href="login.html";
}
},"json");
});
</script>
- 后续相关页面的安全校验,如果需要token才能访问的页面
从存储的地方取出token,来进行校验,没有token说明没有登录则返回登录界面
<script>
var data = sessionStorage.getItem("token");
if(data==null){
window.location.href="login.html";
}
</script>
补充:sessionStorage和localStorage
代码 | 含义 |
---|---|
window.sessionStorage(会话存储) | 暂时储存,浏览器关闭之后会清除 |
window.localStorage (本地存储) | 本地储存,浏览器关闭之后依旧不会清除,只能人为删除 |
平时储存的话建议使用sessionStorage;
开启Springboot拦截器引用上述校验token的代码
/**
* 登录拦截
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getParameter("token");
try {
JwtParser parser = Jwts.parser();
parser.setSigningKey("WuHan");
Jws<Claims> claimsJws = parser.parseClaimsJws(token);
Claims body = claimsJws.getBody();
String username = body.getSubject();
Object role = body.get("role");
return true;
} catch (ExpiredJwtException e) {
e.printStackTrace();
} catch (UnsupportedJwtException e) {
e.printStackTrace();
} catch (MalformedJwtException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
加载拦截器
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Resource
private LoginInterceptor loginInterceptor;
/**
* 添加拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//.execudePathPatterns()//可以添加不拦截的地址
registry.addInterceptor(loginInterceptor).addPathPatterns("/**");
}
}
The best investment is to invest in yourself
2020.06.03 记录辰兮的第75篇博客