springboot+shiro+jwtsession和token進行身份驗證和授權(quán)
最近和別的軟件集成項目,需要提供給別人接口來進行數(shù)據(jù)傳輸,發(fā)現(xiàn)給他token后并不能訪問我的接口,拿postman試了下還真是不行。檢查代碼發(fā)現(xiàn)項目的shiro配置是通過session會話來校驗信息的 ,我之前一直是前后端自己寫,用瀏覽器來調(diào)試的程序所以沒發(fā)現(xiàn)這個問題。
瀏覽器請求頭的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;
}
@Override
public String getHost() {
return null;
}
@Override
public boolean isRememberMe() {
return false;
}
@Override
public Object getPrincipal() {
return token;
}
@Override
public 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);
@Override
protected 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
*/
@Override
protected 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 {
// 校驗jwt
try {
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);
}
}
@Override
protected 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
*/
@Override
protected 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;
}else {
// session方式
return super.preHandle(servletRequest, response);
}
}
/* 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 {
@Bean
public 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("${spring.redis.host}")
private String redisHost;
@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(c));
// 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 授權(quán)
* @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 {
/**
* 加密的秘鑰,相當于服務(wù)器私鑰,一定保管好,不能泄露
*/
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 {
@Autowired
private SysUserService sysUserService;
@Override
public 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;
}
}
}8.總結(jié)
在Spring Boot, Shiro和JWT的項目中,可以同時使用session和token進行身份驗證和授權(quán),但通常token用于無狀態(tài)的RESTful API,而session用于長連接的情況,如Web應(yīng)用。
- springboot實現(xiàn)token驗證登陸狀態(tài)的示例代碼
- SpringBoot整合JWT(JSON?Web?Token)生成token與驗證的流程及示例
- SpringBoot集成JWT實現(xiàn)Token登錄驗證的示例代碼
- SpringBoot登錄驗證token攔截器的實現(xiàn)
- 實戰(zhàn)SpringBoot集成JWT實現(xiàn)token驗證
- Springboot 如何實現(xiàn)filter攔截token驗證和跨域
- SpringBoot整合JWT框架,解決Token跨域驗證問題
- SpringBoot集成JWT實現(xiàn)token驗證的流程
- SpringBoot下token短信驗證登入登出權(quán)限操作(token存放redis,ali短信接口)
- Spring boot+VUE實現(xiàn)token驗證的示例代碼
相關(guān)文章
Java8通過Function獲取字段名的方法(獲取實體類的字段名稱)
Java8通過Function獲取字段名。不用再硬編碼,效果類似于mybatis-plus的LambdaQueryWrapper,對Java8通過Function獲取字段名相關(guān)知識感興趣的朋友一起看看吧2021-09-09
MybatisPlus多數(shù)據(jù)源及事務(wù)解決思路
這篇文章主要介紹了MybatisPlus多數(shù)據(jù)源及事務(wù)解決思路,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01
最優(yōu)雅地整合 Spring & Spring MVC & MyBatis 搭建 Java 企業(yè)級應(yīng)用(附源碼)
這篇文章主要介紹了最優(yōu)雅地整合 Spring & Spring MVC & MyBatis 搭建 Java 企業(yè)級應(yīng)用(附源碼),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01
idea運行tomcat報錯找不到catalina.bat,系統(tǒng)找不到指定的文件問題
這篇文章主要介紹了idea運行tomcat報錯找不到catalina.bat,系統(tǒng)找不到指定的文件問題,具有很好的參考價值,希望對大家有所幫助,2023-11-11

