Shiro implements Token authentication

1. Add dependencies

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

2. Hierarchical relationship between shiro objects

3. Custom 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;
    }
}

4. Exception handling in the Filter stage 

        The exception thrown in the Filter phase is handled by Tomcat by default, and a 500 exception is thrown. Since it has not entered the Controller phase, it cannot be caught and processed by the Controller's global exception handler. FIlter in Spring-boot is executed nestedly, so in the first Filter, we can catch the exceptions thrown by all subsequent Filters. If we catch the exception at this time, and then forward the request to a specific ErrorController for exception throwing , at this point we can uniformly handle the exceptions in the Filter stage.

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);
        }
    }
}

 Register a custom ErrorFilter, and place it at the top level.

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;
    }
}

        The exception thrown by Filter in Shiro is javax.servlet.ServletException. If the exception is caused by other exceptions, put it in cause. So the custom exceptions we throw are all in the cause property.

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;
        }
    }
}

 

Guess you like

Origin blog.csdn.net/qq_33235279/article/details/130595586