1.shiro拦截入口就是shiro拦截器。
在springmvc,的时候,shiro的拦截逻辑很简单,就是在web.xml配置相应的shiro拦截器,对请求进行拦截验证是否,
2.springboot中,其实也一样,通过配置注解@Configuration,将ShiroFilterFactoryBean和DefaultWebSecurityManager
注入到spring容器中。其中ShiroFilterFactoryBean这个类中将
JwtFilter添加进去,代码中粉色。DefaultWebSecurityManager,
@Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); // 拦截器 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); if(oConvertUtils.isNotEmpty(excludeUrls)){ String[] permissionUrl = excludeUrls.split(","); for(String url : permissionUrl){ filterChainDefinitionMap.put(url,"anon"); } }
//首页排除 filterChainDefinitionMap.put("/app/userIndex/**", "anon"); //首页接口 filterChainDefinitionMap.put("/app/notify/**", "anon"); //首页接口 filterChainDefinitionMap.put("/app/notifyAliPay/**", "anon"); //首页接口 //websocket排除 filterChainDefinitionMap.put("/websocket/**", "anon"); filterChainDefinitionMap.put("/newsWebsocket/**", "anon"); // 添加自己的过滤器并且取名为jwt Map<String, Filter> filterMap = new HashMap<String, Filter>(1); //如果cloudServer为空 则说明是单体 需要加载跨域配置 Object cloudServer = env.getProperty(CommonConstant.CLOUD_SERVER_KEY); filterMap.put("jwt", new JwtFilter(cloudServer==null)); shiroFilterFactoryBean.setFilters(filterMap); // <!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 filterChainDefinitionMap.put("/**", "jwt"); // 未授权界面返回JSON shiroFilterFactoryBean.setUnauthorizedUrl("/sys/common/403"); shiroFilterFactoryBean.setLoginUrl("/sys/common/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean("securityManager") public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm); /* * 关闭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); //自定义缓存实现,使用redis securityManager.setCacheManager(redisCacheManager()); return securityManager; }
JwtFilter类
package org.jeecg.config.shiro.filters; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; import org.jeecg.common.constant.CommonConstant; import org.jeecg.config.mybatis.TenantContext; import org.jeecg.config.shiro.JwtToken; 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.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Date; /** * @Description: 鉴权登录拦截器 * @Author: Scott * @Date: 2018/10/7 **/ @Slf4j public class JwtFilter extends BasicHttpAuthenticationFilter { private boolean allowOrigin = true; public JwtFilter(){} public JwtFilter(boolean allowOrigin){ this.allowOrigin = allowOrigin; } /** * 执行登录认证 * * @param request * @param response * @param mappedValue * @return */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { try { executeLogin(request, response); return true; } catch (Exception e) { throw new AuthenticationException("Token失效,请重新登录", e); } } /** * */ @Override protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { HttpServletRequest httpServletRequest = (HttpServletRequest) request; String token = httpServletRequest.getHeader(CommonConstant.X_ACCESS_TOKEN); JwtToken jwtToken = new JwtToken(token); // 提交给realm进行登入,如果错误他会抛出异常并被捕获 getSubject(request, response).login(jwtToken); // 如果没有抛出异常则代表登入成功,返回true return true; } /** * 对跨域提供支持 */ @Override protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; if(allowOrigin){ httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin")); httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE"); httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers")); //update-begin-author:scott date:20200907 for:issues/I1TAAP 前后端分离,shiro过滤器配置引起的跨域问题 // 是否允许发送Cookie,默认Cookie不包括在CORS请求之中。设为true时,表示服务器允许Cookie包含在请求中。 httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); //update-end-author:scott date:20200907 for:issues/I1TAAP 前后端分离,shiro过滤器配置引起的跨域问题 } // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态 if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) { httpServletResponse.setStatus(HttpStatus.OK.value()); return false; } //update-begin-author:taoyan date:20200708 for:多租户用到 String tenant_id = httpServletRequest.getHeader(CommonConstant.TENANT_ID); TenantContext.setTenant(tenant_id); //update-end-author:taoyan date:20200708 for:多租户用到 try{ return super.preHandle(request, response); }catch (Exception e){ httpServletResponse.setStatus(HttpStatus.OK.value()); JSONObject data = new JSONObject(); data.put("code", 403); data.put("message", "Token失效,请重新登录"); data.put("success", false); data.put("now", new Date().getTime()); /**获取OutputStream输出流*/ OutputStream outputStream = response.getOutputStream(); /**设置json返回格式*/ ((HttpServletResponse) response).setHeader("content-type", "application/json"); /**将字符转换成字节数组,指定以UTF-8编码进行转换*/ byte[] dataByteArr = data.toJSONString().getBytes(StandardCharsets.UTF_8); /**使用OutputStream流向客户端输出字节数组*/ outputStream.write(dataByteArr); return false; } //return super.preHandle(request, response); } }