当前位置: 代码网 > it编程>编程语言>Java > SpringBoot3.0集成Jwt实现token验证过程

SpringBoot3.0集成Jwt实现token验证过程

2025年12月24日 Java 我要评论
一、pom.xml引入依赖<dependency> <groupid>io.jsonwebtoken</groupid> <art

一、pom.xml引入依赖

<dependency>
        <groupid>io.jsonwebtoken</groupid>
        <artifactid>jjwt</artifactid>
        <version>0.9.1</version>
    </dependency>
    <dependency>
        <groupid>javax.xml.bind</groupid>
        <artifactid>jaxb-api</artifactid>
        <version>2.3.1</version>
</dependency>

二、创建工具类jwtutils

public class jwtutils {
    private static final hashmap<string, string> _audiences = new hashmap<>();

    public static string createjwt(userproviderdto user) {
        // 指定签名的时候使用的签名算法,也就是header那部分
        signaturealgorithm signaturealgorithm = signaturealgorithm.hs256;

        hashmap<string, object> claims = new hashmap<>();
        claims.put(const.jwt_claims_user, json.tojsonstring(user));

        // 设置jwt的body
        jwtbuilder builder = jwts.builder()
                .setclaims(claims)
                .setsubject(user.getusername())
                .setaudience(updateaudience(user.getusername()))
                .setissuedat(new date())
                // 设置签名使用的签名算法和签名使用的秘钥
                .signwith(signaturealgorithm, const.jwt_secret_key.getbytes(standardcharsets.utf_8))
                // 设置过期时间
                .setexpiration(timeutils.gettodayend());

        return builder.compact();
    }

    /**
     * token解密
     */
    public static claims parsejwt(string token) {
        try {
            // 得到defaultjwtparser
            return jwts.parser()
                    // 设置签名的秘钥
                    .setsigningkey(const.jwt_secret_key.getbytes(standardcharsets.utf_8))
                    // 设置需要解析的jwt
                    .parseclaimsjws(token)
                    .getbody();
        }
        catch (exception ex) {
            return null;
        }
    }

    /**
     * 更新当前登录账号的最新状态
     * @param userno 用户名
     * @return 状态标志
     */
    public static string updateaudience(string userno) {
        if (logwingstringutils.isnullorempty(userno))
            return "";

        var audience = userno + timeutils.getcurrenttimestamp();
        _audiences.put(userno, audience);

        return audience;
    }

    /**
     * 判断当前token是否最新的,不是需重新登录
     * @return boolean
     */
    public static boolean isnewestaudience(string userno, string audience) {
        if (logwingstringutils.isnullorempty(userno) || logwingstringutils.isnullorempty(audience))
            return false;

        if (!_audiences.containskey(userno))
            return false;

        return _audiences.get(userno).equals(audience);
    }

    /**
     * 获取token
     * @param request 当前http请求
     * @return token
     */
    public static string gettoken(httpservletrequest request) {
        string token = request.getheader("authorization");
        if (logwingstringutils.isnullorempty(token)) {
            return "";
        }

        token = token.replace("bearer ", "");
        if (logwingstringutils.isnullorempty(token)) {
            return "";
        }

        return token;
    }
}

三、添加全局拦截器,校验token是否有效

public class jwtinterceptor implements handlerinterceptor {
    //主要功能拦截请求验证token
    @override
    public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
        string token = jwtutils.gettoken(request);
        if (logwingstringutils.isnullorempty(token)) {
            throw new invaliduserexception();
        }

        var claims = jwtutils.parsejwt(token);
        if (claims == null) {
            throw new invaliduserexception();
        }

        date createtime = claims.getissuedat();
        if (createtime == null || createtime.compareto(timeutils.gettodaystart()) < 0) {
            throw new invaliduserexception();
        }

        string audience = claims.getaudience();
        if (!jwtutils.isnewestaudience(claims.getsubject(), audience)) {
            throw new invaliduserexception();
        }

        return true;
    }
}

四、将拦截器添加到webconfig配置中

拦截请求进行token校验

@configuration
public class webconfig implements webmvcconfigurer {
    
    @override
    public void addinterceptors(interceptorregistry registry) {
        registry.addinterceptor(new jwtinterceptor())
                //拦截的路径
                .addpathpatterns("/**")
                //不需要的拦截请求
                .excludepathpatterns(
                        "/api/oauth/login",
                        "/api/oauth/logout",
                        "/api/getdate",
                        "/swagger-ui/**",
                        "/v3/**");
    }
}

五、登录接口增加生成token

返回前端,账号密码校验完成之后生成token,将用户基本信息存入token

public loginoutputdto login(loginuserinputdto input) {
        admin loginuser = adminrepository.getloginuserbyusername(input.getusername());
        if (loginuser == null)
            return new loginoutputdto(false, "用户名不存在,请重新输入。");

        var inputpassword = rsautils.decryptbyrsa(input.getencryptedpassword());
        var encryptpassword = md5utils.encryptmd5(md5utils.encryptmd5(inputpassword) + loginuser.getsalt());
        if (!encryptpassword.equals(loginuser.getpassword()))
            return new loginoutputdto(false, "密码有误,请重新输入。");

        userproviderdto userprovider = getuserprovider(loginuser);
        string token = jwtutils.createjwt(userprovider);
        loginoutputdto output = new loginoutputdto(true, "登录成功。");
        output.setaccesstoken(token);

        return output;
    }

    private userproviderdto getuserprovider(admin loginuser) {
        userproviderdto userprovider = new userproviderdto();
        userprovider.setid(loginuser.getid());
        userprovider.setusername(loginuser.getusername());
        userprovider.setstatus(loginuser.getstatus());
        userprovider.settype(loginuser.gettype());

        return userprovider;
    }
}

六、定义userprovider

提供从token解析出当前用户信息的接口方法

@service
public class userprovider implements iuserprovider {
    @autowired
    private httpservletrequest httpservletrequest;

    @override
    public userproviderdto getcurrentuser() {
        string token = jwtutils.gettoken(httpservletrequest);
        if (logwingstringutils.isnullorempty(token)) {
            throw new runtimeexception(const.un_login_tips);
        }

        claims claims = jwtutils.parsejwt(token);
        if (claims == null) {
            throw new runtimeexception(const.un_login_tips);
        }

        date createtime = claims.getissuedat();
        if (createtime == null || createtime.compareto(timeutils.gettodaystart()) < 0) {
            throw new runtimeexception(const.un_login_tips);
        }

        string user = (string) claims.get(const.jwt_claims_user);

        return json.parseobject(user, userproviderdto.class);
    }
}

七、定义controller接口

登录接口

@postmapping("/api/oauth/login")
@responsebody
@operation(summary = "登录授权接口", description = "登录授权接口")
public loginoutputdto login(@valid @requestbody loginuserinputdto input) throws nosuchalgorithmexception {
    return loginservice.login(input);
}

退出接口

退出时更新当前账号的audience,用来避免退出后用旧的token仍然可以通过接口token是否有效的拦截(因为jwt具有无状态性,生成token之后改token只能等设置的过期时间到了之后才能自动销毁,否则该token在过期时间之前仍然有效),具体方法见jwtutils中的updateaudience和isnewestaudience方法

@postmapping("/api/oauth/logout")
@responsebody
@operation(summary = "退出接口")
public responseoutputdto logout() {
    var user = userprovider.getcurrentuser();
    jwtutils.updateaudience(user.getusername());

    return new responseoutputdto();
}

八、自定义异常invaliduserexception

并拦截全局异常invaliduserexception,捕捉拦截器抛出的异常,返回给前端

public class invaliduserexception extends exception {
    public invaliduserexception() {
        super(const.un_login_tips);
    }
}
@controlleradvice
@slf4j
public class systemexceptionhandler {
    @exceptionhandler(invaliduserexception.class)
    @responsebody
    public loginoutputdto doexception(invaliduserexception ex) {
        return new loginoutputdto(false, ex.getmessage());
    }

    @exceptionhandler(exception.class)
    @responsebody
    public responseoutputdto doexception(exception ex) {
        if (ex.getmessage().contains("未登录")) {
            return new responseoutputdto(false, ex.getmessage());
        }

        log.error(ex.getmessage(), ex);
        return new responseoutputdto(false, "程序发生错误,请联系客服。");
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com