当前位置: 首页 > news >正文

springboot+shiro+jwt 兼容session和token

最近和别的软件集成项目,需要提供给别人接口来进行数据传输,发现给他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;}@Overridepublic String getHost() {return null;}@Overridepublic boolean isRememberMe() {return false;}@Overridepublic Object getPrincipal() {return token;}@Overridepublic 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);@Overrideprotected 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*/@Overrideprotected 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 {// 校验jwttry {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);}}@Overrideprotected 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*/@Overrideprotected 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;}/*    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 {@Beanpublic 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(“ s p r i n g . r e d i s . h o s t " ) p r i v a t e S t r i n g r e d i s H o s t ; @ V a l u e ( " {spring.redis.host}") private String redisHost; @Value(" spring.redis.host")privateStringredisHost;@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©);
// 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 {@Autowiredprivate SysUserService sysUserService;@Overridepublic 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;}}
}

相关文章:

  • mac m芯片安装win11遇坑
  • MySQL 之 JSON 支持(三)—— JSON 函数
  • Centos7.9部署单节点K8S环境
  • 计算机网络之网络层知识总结
  • 前端HTML相关知识
  • 【名词解释】Unity中的3D物理系统:碰撞体
  • 用宝塔部署vue+springboot上线公网详细步骤
  • 模拟面试题卷一
  • 桌面应用开发框架比较:Electron、Flutter、Tauri、React Native 与 Qt
  • 25.梯度消失和梯度爆炸
  • 双链表——AcWing.827双链表
  • 2024年华为OD机试真题-考古学家-C++-OD统一考试(C卷D卷)
  • <Linux>进程
  • 大模型网信办备案全网最详细流程【附附件】
  • 【原创】springboot+mysql小区用水监控管理系统设计与实现
  • (三)从jvm层面了解线程的启动和停止
  • 【node学习】协程
  • ➹使用webpack配置多页面应用(MPA)
  • ESLint简单操作
  • gf框架之分页模块(五) - 自定义分页
  • leetcode-27. Remove Element
  • mysql中InnoDB引擎中页的概念
  • SpringBoot几种定时任务的实现方式
  • SQLServer之创建显式事务
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 前端性能优化——回流与重绘
  • 数组大概知多少
  • 我建了一个叫Hello World的项目
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • postgresql行列转换函数
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • 积累各种好的链接
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • #mysql 8.0 踩坑日记
  • #pragma pack(1)
  • $jQuery 重写Alert样式方法
  • (145)光线追踪距离场柔和阴影
  • (AngularJS)Angular 控制器之间通信初探
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (三)uboot源码分析
  • (一)80c52学习之旅-起始篇
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转)从零实现3D图像引擎:(8)参数化直线与3D平面函数库
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • ./indexer: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object fil
  • .NET 4.0中的泛型协变和反变
  • .NET C# 操作Neo4j图数据库
  • .Net 代码性能 - (1)
  • .net流程开发平台的一些难点(1)
  • .NET中的Exception处理(C#)
  • /etc/fstab 只读无法修改的解决办法