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. PHP:第五章——字符串与数组及其他函数

    <?php header("Content-Type:text/html;charset=utf-8"); //1.str_split——将字符串转换为数组. /*$str= ...

  2. ActiveX开发

    转自(http://blog.csdn.net/mingojiang/article/details/8159263) 一.ActiveX基础 1.1什么是ActiveX ActiveX是COM规范的 ...

  3. Double H4.0

    Double H4.0 修改完善已提交的需求规格说明书 https://docs.qq.com/doc/DTGxWRkh6c3ZLVldq?tdsourcetag=s_pcqq_file_edit&a ...

  4. Python3 字典Dict(十三)

    Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度. 字典是另一种可变容器模型,且可存储任意类 ...

  5. 免费获取半年 Bitdefender Total Security 2014

    免费获取半年 Bitdefender Total Security 2014,安装后剩余 200 天使用期.安装程序语言是德语,调包下安装语言应该也是可以的? 目前德国活动,Bitdefender T ...

  6. kmp算法中的next数组实例解释

    假设求串′ababaaababaa′的next数组 模式串 a b a b a a a b a b a a 下标 1 2 3 4 5 6 7 8 9 10 11 12 1.前两位:next数组前两位一 ...

  7. Linux下各文件夹的含义和用途

    Linux根目录”/“下各个系统文件夹的含义和用途 1./boot 该目录默认下存放的是Linux的启动文件和内核. 2./initrd 它的英文含义是boot loader initialized ...

  8. L206

    There are so many new books about dying that there are now special shelves set aside forthem in book ...

  9. HTML学习3---排版标记

    上节,我们学习了boda常用的属性以及HTML的一些标记,但是图显示的效果却不是那么的好看. 原因就是没有排版好,我们这次使用居中来使这个页面更好看一点,顺便多加入几个别的标记. HTML排版标记 ( ...

  10. Java SE 8 流库(一)

    1. 流的作用 通过使用流,说明想要完成什么任务,而不是说明如何去实现它,将操作的调度留给具体实现去解决: 实例:假如我们想要计算某个属性的平均值,那么我们就可以指定数据源和属性,然后,流库就可以对计 ...