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();
// if (isLoginAttempt(request, response)) {
// try {
// executeLogin(request, response);
// } catch (Exception e) {
// response401(response, e.getMessage());
// }
// }
// return true;
}
@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);
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
getSubject(request, response).login(token);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if ("OPTIONS".equals(httpServletRequest.getMethod())){
return true;
}
//完成token登入
//1.检查请求头中是否含有token
String token = httpServletRequest.getHeader(AUTHORIZATION_HEADER);
//2. 如果客户端没有携带token,拦下请求
if (null == token || "".equals(token)) {
response401(response, "认证失败(Unauthorized),无法访问系统资源");
return false;
}
//3. 委托给Realm进行token验证
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无效,请重新登录");
}
// 验证通过,解密获得username,用于和数据库进行对比
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);
// 添加自己的过滤器并且取名为jwt
//自定义权限拦截器roles
Map filterMap = new HashMap<>();
filterMap.put("jwt", new JWTFilter());
// filterMap.put("roles", new CustomisedURLPathMatchingFilter());
factoryBean.setFilters(filterMap);
Map filterRuleMap = new HashMap<>();
//所有路径都被权限及jwt过滤器捕获
filterRuleMap.put("/**", "noSessionCreation,jwt,roles");
// 访问401和404页面不通过我们的Filter
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
欢迎指正不足,共同进步



