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. 001PHP文件处理——文件处理disk_total_space disk_free_space basename dirname file_exists filetype

    <?php /** * 文件处理disk_total_space disk_free_space basename dirname file_exists filetype */ //disk_ ...

  2. Jquery表单清空

    虽然reset方法可以做到一部分,但是如果你有个元素是这样的 <input name="percent" value="50"/> 那么点击rese ...

  3. 『转』Panda Antivirus Pro 2014 – 免费6个月

    Panda Antivirus Pro 2014 为您的计算机提供了最简单的使用和最直观的保护.最近,Panda公司和 softonic公司合作推出免费半年版本活动地址:点此进入点击“Kostenlo ...

  4. protel 99se 全部焊盘和过孔补泪滴,很多都是失败的,对板子有影响吗?补泪滴的作用?

    泪滴     是焊盘与导线或者是导线与导孔之间的滴装连接过度,设置泪滴的目的是在电路板受到巨大外力的冲撞时,避免导线与焊盘或者导线与导孔的接触点断开,另外,设置泪滴也可使PCB电路板显得更加美观.te ...

  5. .net 面试题总结

    1. DataSet和DataReader的区别? DataReader:和数据库处于一直连接状态.只读只能向前读取,一次只能读取一行信息.DataReader每次只在内存中加载一条数据,内存占用少, ...

  6. pixi之加载纹理贴图和精灵类的使用

    因为之前看过three.js的缘故,所以pixi学习起来也是很快的,主要就是熟悉pixi的API,所以,在这里记录一下pixi常用API,废话不多说,下面上干货. 一.为你的PIXI场景添加图片(精灵 ...

  7. dir listing 目录文件列表索引

    一般而言,网站应用都有一个入口,比如说:index.php,index.html,app.js等.通过这个路口,以及相应的路由功能,去到网站各个功能版块. 而网站的目录结构,目录里面的文件列表,一般都 ...

  8. [leetcode] 204. Count Primes 统计小于非负整数n的素数的个数

    题目大意 https://leetcode.com/problems/count-primes/description/ 204. Count Primes Count the number of p ...

  9. CNN中卷积层的计算细节

    原文链接: https://zhuanlan.zhihu.com/p/29119239 卷积层尺寸的计算原理 输入矩阵格式:四个维度,依次为:样本数.图像高度.图像宽度.图像通道数 输出矩阵格式:与输 ...

  10. Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.elasticsearch.threadpool.ThreadPool

    springboot中遇到的, 将guava添加到项目中即可.(当时添加的是guava 18)