拦截器中解析OAuth2身份认证,保存用户信息至SecurityContextHolder当前线程

1.首先定义拦截器,在前置拦截获取认证信息,取出UserDetails中保存的username,通过mapper接口查询数据库用户信息对象,保存至SecurityContextHolderd 当前线程,以便后续随时在程序中获取用户信息

package com.youdu.wuhan2020.config;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.youdu.wuhan2020.entity.SysUser;
import com.youdu.wuhan2020.mapper.SysUserMapper;
import com.youdu.wuhan2020.utils.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import java.util.Collection;
import java.util.List;

/**
 * 定义一个Interceptor 非常简单方式也有几种,我这里简单列举两种 1、类 要实现Spring 的HandlerInterceptor 接口 2、类
 * 继承实现了HandlerInterceptor 接口的类,例如 已经提供的实现了HandlerInterceptor
 * 接口的抽象类HandlerInterceptorAdapter
 *
 * @author 刘彦军 !!!除了定义此类,还需要将自定义的拦截器,注册到WebMvcConfigurer中,拦截器才起作用
 * 之前我们在xml中配置拦截路径等,springboot我们需要继承WebMvcConfigurerAdapter类
 * 不过springBoot2.0以上 WebMvcConfigurerAdapter 方法过时, 有两种替代方案:
 * 1、继承WebMvcConfigurationSupport 2、实现WebMvcConfigurer
 * 但是继承WebMvcConfigurationSupport会让Spring-boot对mvc的自动配置失效。根据项目情况选择。
 * 现在大多数项目是前后端分离,并没有对静态资源有自动配置的需求所以继承WebMvcConfigurationSupport也未尝不可。
 */
@Slf4j
@Component
public class TokenHandlerInterceptor implements HandlerInterceptor {
     
    //需要提前以@bean的方式注入拦截器,否则sysUserMapper为null;
    //原因是因为拦截器的加载在springcontext之前,所以自动注入的mapper是null
    @Autowired
    private SysUserMapper sysUserMapper;

    // 最终拦截
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
     
        //log.info("最终拦截--在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行,主要是用于进行资源清理工作");
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }

    // 后置拦截+统计总访问数
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
     
        //log.info("后置拦截--请求处理之后进行调用,但是在视图被渲染之前,即Controller方法调用之后");
    }

    //
    // 前置拦截
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
     
        //System.out.println("前置拦截在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理");
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication instanceof OAuth2Authentication) {
     
            OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication;
            Authentication userAuthentication = oAuth2Authentication.getUserAuthentication();
            //获取用户的account,这里就是UserDetails类 中返回的SecurityUser的name属性,这个name可以扩展
            String account = userAuthentication.getName();
            SysUser sysUser = new SysUser();
            sysUser.setAccount(account);
            List<SysUser> page = sysUserMapper.page(sysUser, null);
            SysUser sysUser1 = null;
            if (!CollectionUtils.isNullOrEmpty(page)) {
     
                sysUser1 = page.get(0);
            }
            //角色数组
            Collection<? extends GrantedAuthority> authorities = userAuthentication.getAuthorities();
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser1, null, authorities);
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        return true;
    }

}

2.将拦截器以@bean的方式进行注入spring容器,并且在springMVC中注册此拦截器

package com.youdu.wuhan2020.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 其实以前都是继承WebMvcConfigurerAdapter类 不过springBoot2.0以上 WebMvcConfigurerAdapter 方法过时
 * 从源码上可以看出,WebMvcConfigurerAdapter类对WebMvcConfigurer接口进行了实现,不过都是空实现。
 * ,java版本1.8 接口可以定义defult方法,那么WebMvcConfigurerAdapter这个适配类也就没有意义了,所以被抛弃
 * 有两种替代方案:
 * 1、继承WebMvcConfigurationSupport
 * 2、实现WebMvcConfigurer
 * 注意!!!
 * 但是继承WebMvcConfigurationSupport会让Spring-boot对mvc的自动配置失效。
 * 比如说默认静态资源路径的访问会失效,以及swagger的映射路径,等等(继承需要自己重新做静态资源路径的配置)
 * 根据项目情况选择。现在大多数项目是前后端分离,
 * 并没有对静态资源有自动配置的需求所以继承WebMvcConfigurationSupport也未尝不可。
 *
 * @author 刘彦军
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
     

    // 继承WebMvcConfigurerAdapter类,注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
     
        // 传入自定义的拦截器
        registry.addInterceptor(tokenHandlerInterceptor())
                // 拦截所有的请求
                .addPathPatterns("/**");
    }

    @Bean
    public TokenHandlerInterceptor tokenHandlerInterceptor() {
     
        return new TokenHandlerInterceptor();
    }

    //	@Override
    //	public void addResourceHandlers(ResourceHandlerRegistry registry) {
     
    //		registry.addResourceHandler(new String[]{
     "/lyj/**"})
    //		.addResourceLocations(new String[]{"classpath:/static/"});
    //		registry.addResourceHandler(new String[]{
     "swagger-ui.html"})
    //				.addResourceLocations(new String[]{
     "classpath:/META-INF/resources/"});
    //		registry.addResourceHandler(new String[]{
     "/webjars/**"})
    //				.addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"});
    //	}

}

3.在程序中取出线程中保存的用户对象,存的时候是sysUser,所有取得时候可以直接转

 SysUser sysUser=(SysUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return "用户name:" +sysUser.getAccount();

你可能感兴趣的:(spring-security)