Shiro实现Token认证

一、添加依赖

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.10.0</version>
        </dependency>

二、shiro各对象层级关系

三、自定义TokenFilter 

package com.example.jiakao.common.shiro;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;

/**
 * 判断密码=>判断token
 */
public class TokenMatcher implements CredentialsMatcher {
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        return false;
    }
}
package com.example.jiakao.common.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class TokenRealm extends AuthorizingRealm{
    public TokenRealm(TokenMatcher matcher){
        super(matcher);
    }
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
    }
}
package com.example.jiakao.common.shiro;

import com.example.jiakao.common.constant.ResultCode;
import com.example.jiakao.common.ehcache.Caches;
import com.example.jiakao.exception.http.ForbiddenException;
import com.example.jiakao.pojo.vo.list.UserInfoVo;
import org.apache.shiro.web.filter.AccessControlFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

/**
 * 验证用户的合法性,是否有相关权限
 */
public class TokenFilter extends AccessControlFilter {
    public static final String HEADER_TOKEN = "Token";
    /**
     * 当请求被TokenFilter拦截时,就会调用这个方法
     * 可以在此方法中做初步判断
     * @param servletRequest
     * @param servletResponse
     * @param o
     * @return 返回True:允许访问,可以进入下一个链条调用;返回False:不允许访问,不会进入下一个链条调用,进入onAccessDenied方法
     * @throws Exception
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
//        HttpServletRequest request = (HttpServletRequest) servletRequest;
        放行所有OPTIONS请求
//        return "OPTIONS".equals(request.getMethod());
        return false;
    }

    /**
     * 当isAccessAllowed返回false时进入该方法
     * 在这个方法中进行token校验
     * @param servletRequest
     * @param servletResponse
     * @return 返回true:允许访问,进入下一链条调用;返回false:拒绝访问,不会进入下一链条调用
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception{
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String token = request.getHeader(HEADER_TOKEN);
        if(token == null){
            throw new ForbiddenException(ResultCode.LACK_TOKEN);
        }
        UserInfoVo user = Caches.getToken(token);
        if(user == null){
            throw new ForbiddenException(ResultCode.TOKEN_ERROR);
        }
//        TODO:鉴权

        return true;
    }
}
package com.example.jiakao.common.shiro;

import com.example.jiakao.common.filter.ErrorFilter;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
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;

@Configuration
public class ShiroCfg {
    @Bean
    public Realm realm(){
        return new TokenRealm(new TokenMatcher());
    }

    /**
     * 用于告诉shiro如何进行拦截
     * 1、拦截哪些url
     * 2、每个url需要进行哪些filter
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(Realm realm){
//        安全管理器
        DefaultWebSecurityManager mgr = new DefaultWebSecurityManager(realm);
        ShiroFilterFactoryBean filterBean = new ShiroFilterFactoryBean();
        filterBean.setSecurityManager(mgr);
//        设置一些自定义filter
        Map<String, Filter> filters = new HashMap<>();
        filters.put("token",new TokenFilter());
        filterBean.setFilters(filters);

        Map<String,String> urlMap = new LinkedHashMap<>();
//        anon匿名访问->不需要登录就可以访问,顺序越靠前,优先级越高
        urlMap.put("/user/login","anon");
        urlMap.put("/user/captcha","anon");
        urlMap.put("/*swagger*/**","anon");
        urlMap.put("/v2/api-docs/**","anon");
        urlMap.put("/webjars/**","anon");
//        全局filter的异常处理
        urlMap.put(ErrorFilter.ERROR_URI,"anon");
        urlMap.put("/**","token");
        filterBean.setFilterChainDefinitionMap(urlMap);
        return filterBean;
    }
}

四、Filter阶段异常处理 

        Filter阶段抛出的异常默认由Tomcat处理,抛出500异常,由于未进入到Controller阶段,因此无法由Controller的全局异常处理器进行捕获处理。Spring-boot中FIlter是嵌套执行,所以在第一个Filter中可以捕获后续所有Filter抛出的异常,如果我们在此时对异常进行捕获,然后将请求转发到特定的ErrorController,进行异常抛出,此时我们就可以对Filter阶段的异常进行统一处理。

package com.example.jiakao.common.filter;

import com.example.jiakao.exception.http.HttpException;

import javax.servlet.*;
import java.io.IOException;

/**
 * Filter的doFilter是嵌套执行,所以在第一个Filter中可以捕获后续所有Filter抛出的异常,
 * 然后将其转发给ErrorController,由其抛出,便于全局Controller异常处理
 */
public class ErrorFilter implements Filter {
    public static final String ERROR_URI = "/handleError";
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try{
            chain.doFilter(request,response);
        }catch (Exception e){
            request.setAttribute(ERROR_URI, e);
            // 转发
            request.getRequestDispatcher(ERROR_URI).forward(request,response);
        }
    }
}

 注册自定义ErrorFilter,层级放在顶层。

package com.example.jiakao.common.config;

import com.example.jiakao.common.prop.ProjProperties;
import com.example.jiakao.common.filter.ErrorFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.Filter;

@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
    @Autowired
    private ProjProperties projProperties;
    @Override
    public void addCorsMappings(CorsRegistry registry) {
//                允许跨域访问的接口
        registry.addMapping("/**")
//                允许携带cookie
                .allowCredentials(true)
//                允许跨域共享的域
                .allowedOrigins(projProperties.getCorsOrigins());
//        允许HTTP请求中携带哪些头部信息
//                .allowedHeaders("*")
//        暴露哪些头部信息
//                .exposedHeaders("*")
//                .allowedMethods("GET", "POST")
    }

    /**
     * 注册Filter
     * @return
     */
    @Bean
    public FilterRegistrationBean<Filter> filterRegistrationBean(){
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new ErrorFilter());
        bean.addUrlPatterns("/*");
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}

        Shiro中Filter抛出的异常是 javax.servlet.ServletException,如果异常是由其他异常引起,放在cause中。因此我们抛出的自定义异常都在cause属性中。

package com.example.jiakao.controller;
import com.example.jiakao.common.filter.ErrorFilter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
public class ErrorController {
    @RequestMapping(ErrorFilter.ERROR_URI)
    public void handle(HttpServletRequest request) throws Throwable {
        Exception e = (Exception) request.getAttribute(ErrorFilter.ERROR_URI);
        if(e != null){
            Throwable cause = e.getCause();
            if(cause != null){
                throw cause;
            }
            throw e;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_33235279/article/details/130595586
今日推荐