实现了FactoryBean接口,创建了一个 SpringShiroFilter
对象,在getObject方法中会创建,创建的步骤如下:
org.apache.shiro.spring.web.ShiroFilterFactoryBean#applyGlobalPropertiesIfNecessary applyGlobalPropertiesIfNecessary filterChains
实现了BeanPostProcessor接口,在 postProcessBeforeInitialization
方法中就是获取到ioc容器中的Filter类型的Bean将其设置到filters属性中
public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException { if (bean instanceof Filter) { log.debug("Found filter chain candidate filter '{}'", beanName); Filter filter = (Filter) bean; // applyGlobalPropertiesIfNecessary(filter); //放入到filters属性中 getFilters().put(beanName, filter); } else { log.trace("Ignoring non-Filter bean '{}'", beanName); } return bean; }
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); if ( request.getAttribute(alreadyFilteredAttributeName) != null ) { log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName()); filterChain.doFilter(request, response); } else //noinspection deprecation if (/* added in 1.2: */ !isEnabled(request, response) || /* retain backwards compatibility: */ shouldNotFilter(request) ) { log.debug("Filter '{}' is not enabled for the current request. Proceeding without invoking this filter.", getName()); filterChain.doFilter(request, response); } else { // Do invoke this filter... log.trace("Filter '{}' not yet executed. Executing now.", getName()); request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); try { //过滤器真正执行的逻辑方法 doFilterInternal(request, response, filterChain); } finally { // Once the request has finished, we're done and we don't // need to mark as 'already filtered' any more. request.removeAttribute(alreadyFilteredAttributeName); } } }
doFilterInternal(request, response, filterChain)
,这个方法是执行过滤器的真正逻辑,被子类 AdviceFilter
实现 preHandle
、 postHandle
,这两个方法能在过滤器执行前后做一些事情。 public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { Exception exception = null; try { //在执行下一个过滤器前执行,如果返回的ttrue表示执行下一个过滤器,否则结束执行 boolean continueChain = preHandle(request, response); if (log.isTraceEnabled()) { log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]"); } if (continueChain) { //执行下一个过滤器 executeChain(request, response, chain); } //后置处理,过滤器执行完毕之后执行 postHandle(request, response); if (log.isTraceEnabled()) { log.trace("Successfully invoked postHandle method"); } } catch (Exception e) { exception = e; } finally { //清除 cleanup(request, response, exception); } }
onPreHandle
protected boolean preHandle(ServletRequest request, ServletResponse response)throws Exception { if (this.appliedPaths == null || this.appliedPaths.isEmpty()) { if (log.isTraceEnabled()) { log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately."); } return true; } for (String path : this.appliedPaths.keySet()) { // If the path does match, then pass on to the subclass implementation for specific checks //(first match 'wins'): if (pathsMatch(path, request)) { log.trace("Current requestURI matches pattern '{}'. Determining filter chain execution...", path); Object config = this.appliedPaths.get(path); return isFilterChainContinued(request, response, path, config); } } //no path matched, allow the request to go through: return true; }
用于控制访问资源的权限,该类继承上面的三个类,其中有如下几个重要的方法:
protected abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
:判断当前的访问是否有权限,如果有权限,那么正常继续执行,如果返回false,那么将会执行 onAccessDenied
方法 protected abstract boolean onAccessDenied(ServletRequest request, ServletResponse response)
:isAccessAllowed返回false的时候将会执行,表示当前没有权限访问,需要执行相应的逻辑 public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)
:在过滤器继续执行之前执行的逻辑,其中默认就是调用了isAccessAllowed判断权限,如下: public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)throws Exception { //逻辑或,如果第一条件为false,将会执行第二个方法,反之不执行 return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue); }
isAccessAllowed issueSuccessRedirect
public static final String DEFAULT_USERNAME_PARAM = "username"; public static final String DEFAULT_PASSWORD_PARAM = "password";
/** * 继承AccessControlFilter抽象类,实现权限控制的访问 */ @Slf4j public class LoginFilterextends AccessControlFilter{ private final static String USERNAME="userName"; private final static String PASSWORD="password"; /** * 登录成功的url */ private String successUrl="success"; /** * 登录失败的url */ private String failUrl="fail"; public LoginFilter(String successUrl, String failUrl){ this.successUrl = successUrl; this.failUrl = failUrl; } /** * 判断是否认证成功 */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)throws Exception { Subject subject = SecurityUtils.getSubject(); return subject.isAuthenticated(); } /** * 创建UserNamePasswordToken */ private UsernamePasswordToken createToken(ServletRequest request){ String userName = WebUtils.getCleanParam(request, USERNAME); String password = WebUtils.getCleanParam(request, PASSWORD); return new UsernamePasswordToken(userName,password); } /** * 没有认证成功执行的方法 */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response)throws Exception { Subject subject=SecurityUtils.getSubject(); try { //执行登录 subject.login(createToken(request)); Map<String,String> params=new HashMap<>(); params.put("token", subject.getSession().getId().toString()); //此处需要将token传入重定向的url,因为重定向的url不是匿名访问的,配置的authc,并且还需要将token返回 //如果走的是匿名的url的话,那么从Subject中获取的Session并不是认证成功的 WebUtils.issueRedirect(request, response, successUrl,params); }catch (AuthenticationException ex){ log.info("登录失败",ex); //登录失败,重定向到指定的url WebUtils.issueRedirect(request, response, failUrl); } //不在执行下面的逻辑 return false; } }
/** * 创建登录的过滤器 */ @Bean public LoginFilter loginFilter(){ return new LoginFilter("/user/success", "/user/unauthentic"); } //配置登录的url filterChainDefinitionMap.put("/login", "loginFilter"); //成功的重定向的请求 /** * LoginFilter登录成功重定向的请求 * @return */ @RequestMapping("success") public PageResponse success(String token){ return new PageResponse("登录成功","0",token); } //失败的重定向 /** * 没有登录跳转到的uri * @return */ @GetMapping("/unauthentic") public PageResponse unauthentic(){ return new PageResponse("尚未登录","401"); }