JWT单点登录代码实现(Demo详解)

【辰兮要努力】:hello你好我是辰兮,很高兴你能来阅读,昵称是希望自己能不断精进,向着优秀程序员前行!

博客来源于项目以及编程中遇到的问题总结,偶尔会有读书分享,我会陆续更新Java前端、后台、数据库、项目案例等相关知识点总结,感谢你的阅读和关注,希望我的博客能帮助到更多的人,分享获取新知,大家一起进步!

吾等采石之人,应怀大教堂之心,愿你们奔赴在各自的热爱中…



一、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篇博客

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员可乐丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值