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.单引号.//里面的变量不会被解释 //例 ...

  2. matlab 将一堆文件名读到一个文本里面windows版本里

    有点坑: 在windows下,这么写,打印时也会把\n当成字符串打印出来 path= strcat('/home/wang/Desktop/trainset/num0/',file(i).name,' ...

  3. 服务器大量TIME_WAIT和CLOSE_WAIT的原因及解决办法

    Linux服务器下查看网络连接的状态 netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 它会显示例如下面的信息: ...

  4. Oracle XE自带数据库创建的过程

    Oracle XE自带数据库如何创建的?XE.sql脚本定义实例究竟是怎样的?阅读下文,您可以找到这些问题的答案. Oracle XE自带数据库是如何创建的呢?这是很多人都提到过的问题,下面就为您详细 ...

  5. hibernate流程图

    流程图: 作者: IT程序狮 链接:http://www.imooc.com/article/1296来源:慕课网

  6. bzoj2721樱花——质因数分解

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2721 要推式子! 发现x和y一定都比 n! 大.不妨设 x = n!+k: 则1/x + 1 ...

  7. The type org.springframework.context.support.AbstractApplicationContext cannot be resolved

    在 myeclipse中,使用 jdk6和7,并使用 spring-framework-5.0.2.RELEASE 时,编写代码: import org.springframework.context ...

  8. java中的变量和常量

    也可以先声明后赋值  自动类型转换 1.  目标类型能与源类型兼容,如 double 型兼容 int 型,但是 char 型不能兼容 int 型 2.  目标类型大于源类型,如 double 类型长度 ...

  9. WindowsServer2012显示计算机的方法

    WindowsServer2012显示计算机的方法 (转) 消失的"计算机"?[这周九叔工作比较忙,还有其他琐事缠身,因此SystemCenter2012SP1系列的发布稍慢,抱歉 ...

  10. 编译sass,遇到报错error style.scss (Line 3: Invalid GBK character "\xE5")

    今天学习sass,写了一行中文注释,结果却遇到了报错: cmd.exe /D /C call C:/Ruby23-x64/bin/scss.bat --no-cache --update style. ...