shiro是使用session验证登录状态,想使用JWT为登录验证,只需要重写shiro的登录后的验证过滤器就能使用JWT
继承BasicHttpAuthenticationFilter过滤器,重写该类中的方法
import com.alibaba.fastjson.JSON;
import com.project.User.entity.Result;
import com.project.config.JWTToken;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpStatus;
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;
public class JWTFilter extends BasicHttpAuthenticationFilter {
protected static final String AUTHORIZATION_HEADER = "Authorization";
private static final Logger log = LogManager.getLogger(JWTFilter.class);
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String authorization = req.getHeader("Authorization");
return authorization != null;
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Subject subject = SecurityUtils.getSubject();
return null != subject && subject.isAuthenticated();
}
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authorization = httpServletRequest.getHeader(AUTHORIZATION_HEADER);
JWTToken token = new JWTToken(authorization);
getSubject(request, response).login(token);
return true;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if ("OPTIONS".equals(httpServletRequest.getMethod())){
return true;
}
String token = httpServletRequest.getHeader(AUTHORIZATION_HEADER);
if (null == token || "".equals(token)) {
response401(response, "认证失败(Unauthorized),无法访问系统资源");
return false;
}
JWTToken jwtToken = new JWTToken(token);
try {
SecurityUtils.getSubject().login(jwtToken);
} catch (AuthenticationException e) {
log.error(e.getMessage());
response401(response, e.getMessage());
return false;
}
return true;
}
private void response401(ServletResponse response, String msg) {
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json; charset=utf-8");
try (PrintWriter out = httpServletResponse.getWriter()) {
String data = JSON.toJSONString(Result.error(HttpStatus.UNAUTHORIZED.value(), msg, null));
out.append(data);
} catch (IOException e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
Realm中,认证方法借鉴
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String token = (String) authenticationToken.getCredentials();
boolean verify = jwtUtil.verify(token);
if (!verify) {
throw new AuthenticationException("token无效,请重新登录");
}
Integer userid = jwtUtil.getIntUser(token,"userid");
if (userid == null) {
throw new AuthenticationException("token无效");
}
String redisToken =(String) redisTemplate.opsForValue().get(RedisPreloadData.LOGIN_TOKEN_BYID + userid);
if(!redisToken.equals(token)){throw new AuthenticationException("已退出登录或其他地方已登录,令牌无效");}
Object redisUserid = redisTemplate.opsForValue().get(RedisPreloadData.LOGIN_USER_INFO + userid);
if (redisUserid == null) {
throw new AuthenticationException("用户不存在");
}
return new SimpleAuthenticationInfo(token, token, this.getName());
}
登录过滤器写好后,还需要在shiro的配置文件中添加该过滤器,ShiroConfig.java
@Bean()
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
Map<String, Filter> filterMap = new HashMap<>();
filterMap.put("jwt", new JWTFilter());
factoryBean.setFilters(filterMap);
Map<String, String> filterRuleMap = new HashMap<>();
filterRuleMap.put("/**", "noSessionCreation,jwt,roles");
filterRuleMap.put("/404", "anon");
filterRuleMap.put("/druid/**", "anon");
filterRuleMap.put("/swagger-ui.html/**", "anon");
filterRuleMap.put("/webjars/**", "anon");
filterRuleMap.put("/swagger-resources/**", "anon");
filterRuleMap.put("/configuration/security", "anon");
filterRuleMap.put("/configuration/ui", "anon");
filterRuleMap.put("/v2/api-docs", "anon");
filterRuleMap.put("/land/common/kaptcha", "anon");
filterRuleMap.put("/land/login", "anon");
filterRuleMap.put("/land/logout", "anon");
factoryBean.setFilterChainDefinitionMap(filterRuleMap);
return factoryBean;
}
注意:shiro配置文件中放行的map顺序不能乱排,按照放心或拦截顺序写
看完整实例代码 码云:https://gitee.com/hydrogenated-oxygen/erpshiro 欢迎指正不足,共同进步
|