当前位置: 代码网 > it编程>编程语言>Java > springboot+shiro+jwtsession和token进行身份验证和授权

springboot+shiro+jwtsession和token进行身份验证和授权

2024年07月02日 Java 我要评论
最近和别的软件集成项目,需要提供给别人接口来进行数据传输,发现给他token后并不能访问我的接口,拿postman试了下还真是不行。检查代码发现项目的shiro配置是通过session会话来校验信息的

最近和别的软件集成项目,需要提供给别人接口来进行数据传输,发现给他token后并不能访问我的接口,拿postman试了下还真是不行。检查代码发现项目的shiro配置是通过session会话来校验信息的 ,我之前一直是前后端自己写,用浏览器来调试的程序所以没发现这个问题。

浏览器请求头的cookie带着jessionid是可以正常访问接口的

那要和别的项目集成,他那边又不是通过浏览器,咋办呢,我这边改造吧,兼容token和session不就行了,下面直接贴改造后的完整代码。

pom加依赖

<dependency>
            <groupid>org.crazycake</groupid>
            <artifactid>shiro-redis</artifactid>
            <version>2.4.2.1-release</version>
        </dependency>
             <dependency>
            <groupid>org.apache.shiro</groupid>
            <artifactid>shiro-all</artifactid>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupid>com.auth0</groupid>
            <artifactid>java-jwt</artifactid>
            <version>3.2.0</version>
        </dependency>

1.jwttoken重写token类型

package com.mes.common.token;

import com.mes.module.user.dto.sysuserdto;
import lombok.data;
import org.apache.shiro.authc.hostauthenticationtoken;
import org.apache.shiro.authc.remembermeauthenticationtoken;
import org.apache.shiro.authc.usernamepasswordtoken;


@data
public class jwttoken implements hostauthenticationtoken, remembermeauthenticationtoken {
    private string token;
    private char[] password;
    private boolean rememberme = false;
    private string host;

    public jwttoken(string token){
        this.token = token;
    }


    @override
    public string gethost() {
        return null;
    }

    @override
    public boolean isrememberme() {
        return false;
    }

    @override
    public object getprincipal() {
        return token;
    }

    @override
    public object getcredentials() {
        return token;
    }
}

2.自定义过滤器 jwtfilter

package com.mes.common.shiro;

import com.alibaba.fastjson.json;
import com.auth0.jwt.interfaces.claim;
import com.mes.common.token.jwttoken;
import com.mes.common.utils.jwtutils;
import com.mes.common.utils.result;
import org.apache.commons.lang3.stringutils;
import org.apache.shiro.securityutils;
import org.apache.shiro.authc.authenticationexception;
import org.apache.shiro.authc.authenticationtoken;
import org.apache.shiro.subject.subject;
import org.apache.shiro.web.filter.authc.authenticatingfilter;
import org.apache.shiro.web.util.webutils;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.http.httpstatus;
import org.springframework.web.bind.annotation.requestmethod;

import javax.servlet.servletrequest;
import javax.servlet.servletresponse;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
import java.io.printwriter;
import java.util.map;

/**
 * @description 自定义过滤器
 * @date 2021/8/18
 **/
public class jwtfilter extends authenticatingfilter {

    private static final logger log = loggerfactory.getlogger(jwtfilter.class);
    @override
    protected authenticationtoken createtoken(servletrequest servletrequest, servletresponse servletresponse) throws exception {
        httpservletrequest request = (httpservletrequest) servletrequest;
        string token = request.getheader("token");
        if (token == null){
            return null;
        }
        return new jwttoken(token);
    }


    /**
     * 拦截校验  没有登录的情况下会走此方法
     * @param servletrequest
     * @param servletresponse
     * @return
     * @throws exception
     */
    @override
    protected boolean onaccessdenied(servletrequest servletrequest, servletresponse servletresponse) throws exception {
        httpservletrequest request = (httpservletrequest) servletrequest;
        httpservletresponse response = (httpservletresponse) servletresponse;
        string token = request.getheader("token");

        response.setcontenttype("application/json;charset=utf-8");
        response.setheader("access-control-allow-credentials", "true");
        response.setheader("access-control-allow-origin", request.getheader("origin"));
        response.setheader("access-control-allow-methods", "get,put,delete,update,options");
        response.setheader("access-control-allow-headers", request.getheader("access-control-request-headers"));

        subject subject = getsubject(servletrequest,servletresponse);

        if (!subject.isauthenticated()){
            // 未登录
            printwriter writer = response.getwriter();
            writer.print(json.tojsonstring(new result<>().setcode(402).setmsg("请先登录")));
            return false;
        }

        if (stringutils.isempty(token)){
            printwriter writer = response.getwriter();
            writer.print(json.tojsonstring(new result<>().setcode(402).setmsg("请先登录")));
            return false;
        }else {
            // 校验jwt
            try {
                map<string, claim> claimmap = jwtutils.verifytoken(token);
            } catch (exception e) {
                e.printstacktrace();
                printwriter writer = response.getwriter();
                writer.write(json.tojsonstring(new result<>().setcode(402).setmsg("登录失效,请重新登录")));
                return false;
            }
            return executelogin(servletrequest, servletresponse);
        }
    }
    @override
    protected boolean onloginfailure(authenticationtoken token, authenticationexception e, servletrequest request, servletresponse response) {

        httpservletresponse httpservletresponse = (httpservletresponse) response;

        throwable throwable = e.getcause() == null ? e : e.getcause();

        result result = new result().err().setmsg(e.getmessage());
        string json = json.tojsonstring(result);

        try {
            httpservletresponse.getwriter().print(json);
        } catch (ioexception ioexception) {

        }
        return false;
    }

    /**
     * 跨域支持
     * @param servletrequest
     * @param response
     * @return
     * @throws exception
     */
    @override
    protected boolean prehandle(servletrequest servletrequest, servletresponse response) throws exception {
        httpservletrequest httprequest = webutils.tohttp(servletrequest);
        httpservletresponse httpresponse = webutils.tohttp(response);
        if (httprequest.getmethod().equals(requestmethod.options.name())) {
            httpresponse.setheader("access-control-allow-credentials", "true");
            httpresponse.setheader("access-control-allow-origin", httprequest.getheader("origin"));
            httpresponse.setheader("access-control-allow-methods", "get,put,delete,update,options");
            httpresponse.setheader("access-control-allow-headers", httprequest.getheader("access-control-request-headers"));

            system.out.println(httprequest.getheader("origin"));
            system.out.println(httprequest.getmethod());
            system.out.println(httprequest.getheader("access-control-request-headers"));
            httpresponse.setstatus(httpstatus.ok.value());
            return false;
        }
        httpservletrequest request = (httpservletrequest) servletrequest;
        string token = request.getheader("token");
        if (token != null) {
            try {
//                map<string, claim> claimmap = jwtutils.verifytoken(token);
//                string authtoken = claimmap.get("token").asstring();
                jwttoken jwttoken = new jwttoken(token);
                subject subject = securityutils.getsubject();
                subject.login(jwttoken);
                return true;
            } catch (exception e) {
                e.printstacktrace();
                log.error("token失效,请重新登录");
               response.getwriter().print(json.tojsonstring(new result<>().setcode(402).setmsg("token失效,请重新登录")));
            }
            return false;
        }else {
            // session方式
            return super.prehandle(servletrequest, response);
        }
    }

/*    protected boolean prehandle(servletrequest request, servletresponse response) throws exception {
        httpservletrequest httprequest = webutils.tohttp(request);
        httpservletresponse httpresponse = webutils.tohttp(response);
        if (httprequest.getmethod().equals(requestmethod.options.name())) {
            httpresponse.setheader("access-control-allow-credentials", "true");
            httpresponse.setheader("access-control-allow-origin", httprequest.getheader("origin"));
            httpresponse.setheader("access-control-allow-methods", "get,put,delete,update,options");
            httpresponse.setheader("access-control-allow-headers", httprequest.getheader("access-control-request-headers"));

            system.out.println(httprequest.getheader("origin"));
            system.out.println(httprequest.getmethod());
            system.out.println(httprequest.getheader("access-control-request-headers"));
            httpresponse.setstatus(httpstatus.ok.value());
            return false;
        }
        return super.prehandle(request, response);
    }*/

}

3.配置过滤器 shirofilterregisterconfig

package com.mes.common.config;

import com.mes.common.shiro.jwtfilter;
import org.springframework.boot.web.servlet.filterregistrationbean;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;

/**
 * @description todo
 * @date 2021/8/19
 **/
@configuration
public class shirofilterregisterconfig {
    @bean
    public filterregistrationbean shirologinfilteregistration(jwtfilter filter) {
        filterregistrationbean registration = new filterregistrationbean(filter);
        registration.setenabled(false);
        return registration;
    }
}

4. shiroconfig

package com..mes.common.config;

import com.baomidou.mybatisplus.extension.api.r;
import com..mes.common.constant.exptime;
import com..mes.common.realm.myrealm;
import com..mes.common.shiro.jwtfilter;
import com..mes.common.shiro.mycredentialsmatcher;
import org.apache.shiro.session.mgt.sessionmanager;
import org.apache.shiro.spring.web.shirofilterfactorybean;
import org.apache.shiro.web.mgt.defaultwebsecuritymanager;
import org.apache.shiro.web.session.mgt.defaultwebsessionmanager;
import org.crazycake.shiro.rediscachemanager;
import org.crazycake.shiro.redismanager;
import org.crazycake.shiro.redissessiondao;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.beans.factory.annotation.value;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;

import javax.servlet.filter;
import java.util.hashmap;
import java.util.linkedhashmap;
import java.util.map;

/**
 * @description shiro配置
 * @date 2021/8/18
 **/
@configuration
public class shiroconfig {
    @autowired
    private myrealm myrealm;
    @autowired
    private mycredentialsmatcher mycredentialsmatcher;
    @value("${spring.redis.host}")
    private string redishost;
    @value("${spring.redis.port}")
    private integer redisport;
    @value("${spring.redis.timeout}")
    private integer redistimeout;

//    @bean
//    public defaultwebsessionmanager sessionmanager(@value("${globalsessiontimeout:3600}") long globalsessiontimeout, redismanager c){
//        defaultwebsessionmanager sessionmanager = new defaultwebsessionmanager();
//        sessionmanager.setsessionvalidationschedulerenabled(true);
//        sessionmanager.setsessionidurlrewritingenabled(false);
//        sessionmanager.setsessionvalidationinterval(globalsessiontimeout * 1000);
//        sessionmanager.setglobalsessiontimeout(globalsessiontimeout * 1000);
//        sessionmanager.setsessiondao(redissessiondao(c));
//        return sessionmanager;
//    }
//    @configurationproperties(prefix="spring.redis")
//    @bean
//    public redismanager redismanager() {
//        return new redismanager();
//    }
//    @bean
//    public redissessiondao redissessiondao(redismanager redismanager) {
//        redissessiondao redissessiondao = new redissessiondao();
//        redissessiondao.setredismanager(redismanager);
//        return redissessiondao;
//    }


//    @bean
//    public static defaultadvisorautoproxycreator getdefaultadvisorautoproxycreator(){
//
//        defaultadvisorautoproxycreator defaultadvisorautoproxycreator=new defaultadvisorautoproxycreator();
//        defaultadvisorautoproxycreator.setuseprefix(true);
//
//        return defaultadvisorautoproxycreator;
//    }






    @bean
    public defaultwebsecuritymanager getdefaultwebsecuritymanager(sessionmanager sessionmanager, rediscachemanager rediscachemanager){
        defaultwebsecuritymanager defaultwebsecuritymanager = new defaultwebsecuritymanager();
        myrealm.setcredentialsmatcher(mycredentialsmatcher);
        defaultwebsecuritymanager.setrealm(myrealm);
        defaultwebsecuritymanager.setsessionmanager(sessionmanager);
        defaultwebsecuritymanager.setcachemanager(rediscachemanager);
        return defaultwebsecuritymanager;
    }

    @bean
    public shirofilterfactorybean getshirofilterfactorybean(defaultwebsecuritymanager defaultwebsecuritymanager,jwtfilter jwtfilter){
        shirofilterfactorybean shirofilterfactorybean = new shirofilterfactorybean();
        shirofilterfactorybean.setsecuritymanager(defaultwebsecuritymanager);
//        jwtfilter jwtfilter = new jwtfilter();
        map<string, filter> filtermap = new hashmap<>();
        filtermap.put("jwt",jwtfilter);
        shirofilterfactorybean.setfilters(filtermap);
        map<string,string> map = new linkedhashmap<>();
        map.put("/sys/user/login","anon");
        map.put("/swagger-ui.html**", "anon");
        map.put("/v2/api-docs", "anon");
        map.put("/swagger-resources/**", "anon");
        map.put("/webjars/**", "anon");
        map.put("/img/**","anon");
        map.put("/fastdfs/**","anon");
        map.put("/**","jwt");  //取消就不会拦截
        shirofilterfactorybean.setfilterchaindefinitionmap(map);
//        shirofilterfactorybean.setloginurl("http://192.168.18.17:3000");
        return shirofilterfactorybean;
    }



    @bean
    public jwtfilter getjwtfilter(){
        return new jwtfilter();
    }



    /**
     * 配置shiro redismanager
     * 使用的是shiro-redis开源插件
     * @return
     */
    @bean
    public redismanager redismanager() {
        redismanager redismanager = new redismanager();
        redismanager.sethost(redishost);
        redismanager.setport(redisport);
        redismanager.setexpire(math.tointexact(exptime.exptime));// 配置缓存过期时间
        redismanager.settimeout(redistimeout);
        return redismanager;
    }
    @bean
    public redissessiondao redissessiondao(redismanager redismanager) {
//        redissessiondao redissessiondao = new redissessiondao();
        redissessiondao redissessiondao = new redissessiondao();
        redissessiondao.setredismanager(redismanager);
        return redissessiondao;
    }
    /**
     * shiro session的管理
     */
    @bean
    public defaultwebsessionmanager redissessionmanager(redissessiondao redissessiondao) {
        defaultwebsessionmanager sessionmanager = new defaultwebsessionmanager();
        sessionmanager.setsessiondao(redissessiondao);
        return sessionmanager;
    }
    @bean
    public rediscachemanager rediscachemanager(redismanager redismanager) {
        rediscachemanager rediscachemanager = new rediscachemanager();
        rediscachemanager.setredismanager(redismanager);
        return rediscachemanager;
    }

//    @bean
//    public filterregistrationbean shirologinfilteregistration(jwtfilter filter) {
//        filterregistrationbean registration = new filterregistrationbean(filter);
//        registration.setenabled(false);
//        return registration;
//    }

}

5.自定义认证逻辑 myrealm

package com.mes.common.realm;

import com.auth0.jwt.interfaces.claim;
import com.mes.common.token.jwttoken;
import com.mes.common.utils.jwtutils;
import com.mes.module.user.dto.sysuserdto;
import com.mes.module.user.service.sysuserservice;
import lombok.sneakythrows;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.authorizationinfo;
import org.apache.shiro.authz.simpleauthorizationinfo;
import org.apache.shiro.realm.authorizingrealm;
import org.apache.shiro.subject.principalcollection;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.component;

import java.util.hashmap;
import java.util.map;

/**
 * @description 授权
 * @date 2021/8/18
 **/
@component
public class myrealm extends authorizingrealm {
    @autowired
    private sysuserservice sysuserservice;

    @override
    public boolean supports(authenticationtoken token) {
        return token instanceof jwttoken;
    }

    @override
    protected authorizationinfo dogetauthorizationinfo(principalcollection principalcollection) {
        string username = (string) principalcollection.iterator().next();
        simpleauthorizationinfo info = new simpleauthorizationinfo();
        return info;
    }

    @sneakythrows
    @override
    protected authenticationinfo dogetauthenticationinfo(authenticationtoken authenticationtoken) throws authenticationexception {
        jwttoken jwttoken = (jwttoken) authenticationtoken;
        string token = (string) jwttoken.getprincipal();
        map<string, claim> claimmap = jwtutils.verifytoken(token);
        string username = claimmap.get("name").asstring();
        map<string,object> params = new hashmap<>();
        params.put("username", username);
        sysuserdto userdto = sysuserservice.getone(params);
        if (userdto == null){
            return null;
        }

//        return new simpleauthenticationinfo(userdto,userdto.getpassword(),getname());
        return new simpleauthenticationinfo(userdto,jwttoken,getname());
    }
}

6. token工具类

package com.mes.common.utils;

import com.auth0.jwt.jwt;
import com.auth0.jwt.jwtverifier;
import com.auth0.jwt.algorithms.algorithm;
import com.auth0.jwt.interfaces.claim;
import com.auth0.jwt.interfaces.decodedjwt;
import com.mes.common.constant.exptime;
import io.jsonwebtoken.claims;
import io.jsonwebtoken.jwts;
import lombok.data;

import javax.xml.bind.datatypeconverter;
import java.io.unsupportedencodingexception;
import java.util.date;
import java.util.hashmap;
import java.util.map;


@data
public class jwtutils {
    /**
     * 加密的秘钥,相当于服务器私钥,一定保管好,不能泄露
     */
    private static final string secret = "secret";
    /**
     * token的有效时间,不需要自己验证失效,当失效后,会自动抛出异常
     */
    public static final long exptime = exptime.exptime;

    public static string createtoken(long id, string name, long loginid) throws unsupportedencodingexception {

        map<string, object> map = new hashmap<>();
        map.put("alg", "hs256");
        map.put("typ", "jwt");

        string token = jwt.create()
                .withheader(map)
                .withclaim("id", id)
                .withclaim("name", name)
                .withclaim("loginid", loginid)
                .withexpiresat(new date(system.currenttimemillis() + exptime))
                .withissuedat(new date())
                .sign(algorithm.hmac256(secret));

        return token;
    }

    public static map<string, claim> verifytoken(string token) throws unsupportedencodingexception {
        jwtverifier verifier = jwt.require(algorithm.hmac256(secret)).build();


        decodedjwt jwt = null;

        try {
            jwt = verifier.verify(token);
        } catch (exception e) {
            throw new runtimeexception("登录凭证已过期,请重新登录");
        }
        return jwt.getclaims();
    }

    public static map<string, claim> getclaims(string token) throws unsupportedencodingexception {
        jwtverifier verifier = jwt.require(algorithm.hmac256(secret)).build();

        decodedjwt jwt = null;

        jwt = verifier.verify(token);

        return jwt.getclaims();
    }


}

7.密码验证器

package com.mes.common.shiro;

import com.mes.common.token.jwttoken;
import com.mes.common.utils.commonsutils;
import com.mes.module.user.dto.sysuserdto;
import com.mes.module.user.service.sysuserservice;
import org.apache.shiro.authc.authenticationinfo;
import org.apache.shiro.authc.authenticationtoken;
import org.apache.shiro.authc.credential.simplecredentialsmatcher;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.component;

import java.util.hashmap;
import java.util.map;

/**
 * @description 密码验证器
 * @date 2021/8/18
 **/
@component
public class mycredentialsmatcher extends simplecredentialsmatcher {
    @autowired
    private sysuserservice sysuserservice;

    @override
    public boolean docredentialsmatch(authenticationtoken token, authenticationinfo info) {

        jwttoken jwttoken = (jwttoken) token;
        if (jwttoken.getpassword() == null){
            return true;
        }
        string inpassword = new string(jwttoken.getpassword());
        sysuserdto dto = (sysuserdto) info.getprincipals();
        string username = dto.getusername();
        string dbpassword = string.valueof(info.getcredentials());
        map<string,object> params = new hashmap<>();
        params.put("username",username);
        sysuserdto dbuser = sysuserservice.getone(params);
        string salt = dbuser.getsalt();
        if (commonsutils.encryptpassword(inpassword,salt).equals(dbpassword)){
            return true;
        }else {
            return false;
        }


    }
}

8.总结

在spring boot, shiro和jwt的项目中,可以同时使用session和token进行身份验证和授权,但通常token用于无状态的restful api,而session用于长连接的情况,如web应用。

(0)

相关文章:

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

发表评论

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