1.添加依赖

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>io.fusionauth</groupId>
            <artifactId>fusionauth-jwt</artifactId>
            <version>3.0.0</version>
        </dependency>

2.生成公钥私钥

公钥私钥非必须

可以参考:

FusionAuth/fusionauth-jwt: A simple to use Java 8 JWT Library. Verify, Sign, Encode, Decode all day.
https://github.com/fusionauth/fusionauth-jwt

使用openssl,我本地的环境是在git bash里运行的.

  openssl genrsa -out rsa_private_key.pem
  openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

将这两个文件复制到resources/keys/ 里.

3.主要的代码

3.1 jwt工具

AppConstant.java
public final class AppConstant {

    public static final String JWT_PRIVATE_KEY_PATH = "keys/rsa_private_key.pem";
    public static final String JWT_PUBLIC_KEY_PATH = "keys/rsa_public_key.pem";
    public static final String JWT_ISSUER = "www.demo.com";
    public static final Integer JWT_Expiration = 60;
}
JWTUtil.java
import io.fusionauth.jwt.Signer;
import io.fusionauth.jwt.Verifier;
import io.fusionauth.jwt.domain.JWT;
import io.fusionauth.jwt.rsa.RSASigner;
import io.fusionauth.jwt.rsa.RSAVerifier;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;

@Slf4j
public class JWTUtil {

    /**
     * 生成jwt token.
     *
     * @param subject
     * @return
     */
    public static String genJwt(String subject) {
        File pri = new File(JWTUtil.class.getClassLoader().getResource(AppConstant.JWT_PUBLIC_KEY_PATH).getPath());
        byte[] bFile;
        try {
            bFile = Files.readAllBytes(pri.toPath());
        } catch (IOException e) {
            throw new RuntimeException("检查私钥文件是否存在");
        }
        Signer signer = RSASigner.newSHA256Signer(new String(bFile));

        // 构建 JWT with an issuer(iss), issued at(iat), subject(sub) and expiration(exp)
        JWT jwt = new JWT().setIssuer(AppConstant.JWT_ISSUER)
                .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC))
                .setSubject(subject)
                .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(AppConstant.JWT_Expiration));
        // Sign and encode the JWT to a JSON string representation
        return JWT.getEncoder().encode(jwt, signer);
    }

    /**
     * 解析token.
     */
    public static JWT parse(String encodedJWT) {
        File pub = new File(JWTUtil.class.getClassLoader().getResource(AppConstant.JWT_PRIVATE_KEY_PATH).getPath());
        Verifier verifier = RSAVerifier.newVerifier(pub.toPath());

        // Verify and decode the encoded string JWT to a rich object
        //   * A JWT that is expired or not yet valid will not be decoded, instead a {@link JWTExpiredException} or {@link
        //   * JWTUnavailableForProcessingException} exception will be thrown respectively.
        return JWT.getDecoder().decode(encodedJWT, verifier);
    }

}

3.2 shiro配置

JWTAuthenticationToken.java

import com.example.boot.shirojwt.util.JWTUtil;
import io.fusionauth.jwt.domain.JWT;
import org.apache.shiro.authc.AuthenticationToken;

public class JWTAuthenticationToken implements AuthenticationToken {
    private String token;
    public JWTAuthenticationToken(String token) {
        this.token = token;
    }

    @Override
    public JWT getPrincipal() {
        return JWTUtil.parse(token);
    }

    @Override
    public String getCredentials() {
        return token;
    }
}

JWTTokenRealm.java

import io.fusionauth.jwt.JWTExpiredException;
import io.fusionauth.jwt.JWTUnavailableForProcessingException;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;

import java.util.HashSet;
import java.util.Set;

@Slf4j
@Component
public class JWTTokenRealm extends AuthorizingRealm {
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTAuthenticationToken;
    }

    /**
     * 获取身份验证信息
     * Shiro中,最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。
     *
     * @param authenticationToken 用户身份信息 token
     * @return 返回封装了用户信息的 AuthenticationInfo 实例
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        final JWTAuthenticationToken jwtToken = (JWTAuthenticationToken) authenticationToken;
        try {
            String username = jwtToken.getPrincipal().subject;
            log.debug("[doGetAuthenticationInfo] username:{}", username);
            // 到数据库中查询用户;
            Object user  /*  = xxxDao.selectOne(username) */;
            if (user == null) {
                throw new AccountException("用户不存在");
            } // 处理封号等等逻辑.
        }catch (JWTExpiredException e){
            throw new AccountException("token过期");
        }catch (JWTUnavailableForProcessingException e){
            throw new AccountException("token解析失败");
        }catch (RuntimeException e){
            throw new AccountException("未知异常");
        }

        return new SimpleAuthenticationInfo(jwtToken.getPrincipal(), jwtToken.getCredentials(), "JWTTokenRealm");
    }

    /**
     * 获取授权信息.
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 配置用户的角色 权限
        // principalCollection
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //获得该用户角色
        Set<String> roleSet = new HashSet<>();
        Set<String> permissionSet = new HashSet<>();

        roleSet.add("");
        permissionSet.add("");

        info.setRoles(roleSet);
        info.setStringPermissions(permissionSet);
        return info;
    }}

JWTFilter.java

import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JWTFilter extends BasicHttpAuthenticationFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
        executeLogin(request, response);
        return true;
    }

    /**
     * 执行登陆操作
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        try {
            String token = httpServletRequest.getHeader("Authorization");
            JWTAuthenticationToken jwtToken = new JWTAuthenticationToken(token);
            // 提交给realm进行登入,如果错误 抛出异常并在这里被捕获
            getSubject(request, response).login(jwtToken);
        } catch (RuntimeException e) {
            try {
                // ! 此处的异常不会被全局异常处理捕获到.
                httpServletResponse.sendError(401, "token出错");
            } catch (IOException e1) {
            }
        }
        return true;
    }

}

ShiroConfig.java

import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
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;

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean factory(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

        // 添加自己的过滤器并且取名为jwt
        Map<String, Filter> filterMap = new LinkedHashMap<>();
        //设置我们自定义的JWT过滤器
        filterMap.put("jwt", new JWTFilter());
        factoryBean.setFilters(filterMap);
        factoryBean.setSecurityManager(securityManager);
        Map<String, String> filterRuleMap = new HashMap<>();

        filterRuleMap.put("/user/login", "anon");
        filterRuleMap.put("/**", "jwt");

        factoryBean.setFilterChainDefinitionMap(filterRuleMap);
        return factoryBean;
    }

    @Bean
    public SecurityManager securityManager(JWTTokenRealm jwtTokenRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置自定义 realm.
        securityManager.setRealm(jwtTokenRealm);
        securityManager.setRememberMeManager(null);
        /*
         * 关闭shiro自带的session,详情见文档
         * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
         */
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        return securityManager;
    }

    /**
     * 添加注解.
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

}

3.3 调用

调用jwt工具类生成token (xxx) 返回前端, 前端通过在header中附加 Authorization: xxx

仅是一个大概的集成,其他的地方需要根据自己需要来补充.

springboot+shiro+jwt的更多相关文章

  1. Springboot shiro JWT集成总结

    SpringBoot Shiro JWT 1.建表 DDL.sql CREATE TABLE `t_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, ` ...

  2. SpringBoot+Shiro+JWT前后端分离实现用户权限和接口权限控制

    1. 引入需要的依赖 我使用的是原生jwt的依赖包,在maven仓库中有好多衍生的jwt依赖包,可自己在maven仓库中选择,实现大同小异. <dependency> <groupI ...

  3. 基于Shiro,JWT实现微信小程序登录完整例子

    小程序官方流程图如下,官方地址 : https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html ...

  4. 基于shiro+jwt的真正rest url权限管理,前后端分离

    代码地址如下:http://www.demodashi.com/demo/13277.html bootshiro & usthe bootshiro是基于springboot+shiro+j ...

  5. spring-boot-plus集成Shiro+JWT权限管理

    SpringBoot+Shiro+JWT权限管理 Shiro Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理. 使用Shiro的易于理解的API,您可以 ...

  6. 教你 Shiro + SpringBoot 整合 JWT

    本篇文章将教大家在 shiro + springBoot 的基础上整合 JWT (JSON Web Token) 如果对 shiro 如何整合 springBoot 还不了解的可以先去看我的上一篇文章 ...

  7. Shiro (Shiro + JWT + SpringBoot应用)

    Shiro (Shiro + JWT + SpringBoot应用) 目录 Shiro (Shiro + JWT + SpringBoot应用) 1.Shiro的简介 2.Shiro + JWT + ...

  8. SpringBoot集成JWT 实现接口权限认证

    JWT介绍 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的, 特别适用于分布式站点 ...

  9. Shiro&Jwt验证

    此篇基于 SpringBoot 整合 Shiro & Jwt 进行鉴权 相关代码编写与解析 首先我们创建 JwtFilter 类 继承自 BasicHttpAuthenticationFilt ...

随机推荐

  1. mysqlbinlog初识

    mysql-binlog->解析mysql的binlog日志 mysql的binlog日志是什么? 数据目录下的日下文件就是mysql的binlog日志 mysql-bin.00001 mysq ...

  2. grub2 详解

    grub2详解(翻译和整理官方手册)   分类: Linux 基础篇,Linux 杂项   本文原创地址在博客园:https://www.cnblogs.com/f-ck-need-u/archive ...

  3. Field 'id' doesn't have a default value 原因

    Field 'id' doesn't have a default value昨晚做项目的时候遇到一个问题,在测试数据存储的时候老是报Field 'id' doesn't have a default ...

  4. VScode+Flutter 开发继续踩坑

    运行慢解决方法1:修改build.gradle,注释掉jcenter(),google().使用阿里的镜像.原因是jcenter google库无法访问到导致的问题.虽然我有万能的爬墙工具,开启全局代 ...

  5. spring注解-@Autowired。@Resource。@Service

    Spring的@Autowired注解.@Resource注解和@Service注解 什么是注解 传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点: ...

  6. CF1056:Check Transcription(被hack的hash)

    One of Arkady's friends works at a huge radio telescope. A few decades ago the telescope has sent a ...

  7. 【thrift】thrift详解

    转载:http://zheming.wang/thrift-rpcxiang-jie.html Thrift Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年 ...

  8. 文件和I/O流

    版权声明:本文为[博主](https://zhangkn.github.io)原创文章,未经博主同意不得转载.https://creativecommons.org/licenses/by-nc-sa ...

  9. cocos2dx 安卓真机调试问题汇总

    cocos compile编译apk问题汇总: 1,dx编译报错,没有足够的空间 ANTBUILD : [dx] error : Could not create the Java Virtual M ...

  10. centos下安装docker最新版教程

    1.通过yum安装需要root或者能sudo的权限 yum包更新到最新$ sudo yum update 添加Docker yum源$ sudo tee /etc/yum.repos.d/docker ...