自定义配置拦截器

简介: 自定义配置拦截器

自定义secuirty拦截器



背景


很多时候security默认提供的拦截器往往不够用于我们的日常开发,所以我们经常需要自己重写某些拦截器,达到实现开发的需求


本文,以重写登录拦截器为例


相关源码


/*
在指定筛选器类的位置添加筛选器。例如,如果希望筛选器 CustomFilter 注册到与 相同的 UsernamePasswordAuthenticationFilter位置,则可以调用:
    addFilterAt(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
在同一位置注册多个筛选器意味着它们的排序不是确定的。更具体地说,在同一位置注册多个筛选器不会覆盖现有筛选器。相反,不要注册您不想使用的筛选器。
形参:
过滤器 – 要注册的过滤器
atFilter – 已在 Spring Security 注册(即已知)的另一个 Filter 位置。
返回值:
用于 HttpSecurity 进一步的定制
*/
public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {
    return addFilterAtOffsetOf(filter, 0, atFilter);
}


/*
处理身份验证表单提交。在 Spring Security 3.0 之前调用 AuthenticationProcessingFilter 。
登录表单必须向此筛选器提供两个参数:用户名和密码。要使用的默认参数名称包含在静态字段 SPRING_SECURITY_FORM_USERNAME_KEY 和 SPRING_SECURITY_FORM_PASSWORD_KEY中。还可以通过设置 usernameParameter and passwordParameter 属性来更改参数名称。
默认情况下,此过滤器响应 URL /login。
自:
3.0
作者:
本·亚历克斯、科林·桑帕莱亚努、卢克·泰勒
*/
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login",
                                                                                                            "POST");
    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    private boolean postOnly = true;
    public UsernamePasswordAuthenticationFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }
    public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
    }
}


步骤


一、创建自定义的拦截器类,继承XXX拦截器,实现重写


例如:我们重写了登录的拦截器(表单提交),按照我们需要的方式【需要json格式】,来进行修改自定义的拦截器类


需要做的事情


  • 判断是否为post的请求
  • 判断是否为json格式的数据
  • 将json格式的数据中 获取我们需要的username , password进行认证


/**
 * 自定义前后端分离认证 Filter
 */
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        /**
         * 需要做的三件事
         */
        //1. 判断是否为post的请求
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("请求方法不支持" + request.getMethod());
        }
        //2. 判断是否为json格式的数据
        if (request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) {
            //2.1  提取用户名 ,密码
            try {
                /**
                 * 用流的形式去接收
                 * 因为源码中有
                 private String usernameParameter = "username";
                 private String passwordParameter = "password";
                 所以我们可以直接继承下来 使用
                 */
                Map<String, String> userInfo = new ObjectMapper().readValue(request.getInputStream(), Map.class);
                String username = userInfo.get(getUsernameParameter());
                String password = userInfo.get(getPasswordParameter());
                //已经获取到了username...
                //2.2 按照源码的实例, 我们就需要将获取的内容封装为一个token
                // UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
                setDetails(request, authRequest);
                //最后调用本类中的authenticate
                return this.getAuthenticationManager().authenticate(authRequest);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //3. 将json格式的数据中 获取我们需要的username , password进行认证
        return super.attemptAuthentication(request, response);
    }
}


二、在自定义的安全配置类中进行配置


/**
 * 自定义Filter交给容器进行管理
 * 基本是按照源码中的内容去进行配置
 */
@Bean
public LoginFilter loginFilter() throws Exception {
    LoginFilter loFilter = new LoginFilter();
    loFilter.setFilterProcessesUrl("/doLogin");
    //其中的value属性与 前端form表单中输入用户名的name属性相同
    loFilter.setUsernameParameter("username");
    loFilter.setPasswordParameter("password");
    //注入自己的AuthenticationManager
    loFilter.setAuthenticationManager(authenticationManagerBean());
    //认证成功处理 。认证失败处理
    loFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
        Map<String, Object> result = new HashMap<>();
        result.put("msg", "登录成功!");
        result.put("status", "200");
        result.put("用户信息", (User) authentication.getPrincipal());
        response.setContentType("application/json;charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(result);
        response.getWriter().println(s);
    });
    loFilter.setAuthenticationFailureHandler((request, response, exception) -> {
        Map<String, Object> result = new HashMap<>();
        result.put("msg", "登录失败!!");
        result.put("status", "400");
        result.put("错误信息", exception.getMessage());
        response.setContentType("application/json;charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(result);
        response.getWriter().println(s);
    });
    return loFilter;
}


配置自己的身份认证管理员(AuthenticationManager)


这样做的目的是为了让我们登录时进行验证的数据是从数据库或者缓存中提取的,而不是仅仅放上默认给出的数据。所以这一步是必须要进行操作的


//注入自己的AuthenticationManager
loFilter.setAuthenticationManager(authenticationManagerBean());


重写UserDetails


@Component
public class MyUserDetailsService implements UserDetailsService {
    private final UserDao userDao;
    @Autowired
    public MyUserDetailsService(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userDao.loadUserByUsername(username);
        if (ObjectUtils.isEmpty(user)) throw new UsernameNotFoundException("用户名不正确!");
        //存在的话 , 赋予权限信息
        List<Role> roles = userDao.getRolesByUid(user.getId());
        user.setRoles(roles);
        return user;
    }
}

实现注入


将自己重写的DetailsService进行依赖注入,然后交给AuthenticationManagerBuilder


/**
 * 创建自己userDetailsService,然后交给AuthenticationManagerBuilder来创建我们自己的AuthenticationManager
 */
private final MyUserDetailsService myUserDetailsService;
@Autowired
public testConfig(MyUserDetailsService myUserDetailsService) {
    this.myUserDetailsService = myUserDetailsService;
}


创建自定义的AuthenticationManager,管理自己的DetailsService


* 创建自己的AuthenticationManager
 */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(myUserDetailsService);
}


将自定义的认证放到容器中,覆盖默认的


/**
 * 将自定义的认证暴露在工厂中 (加入到容器中去管理)
 */
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}


三、在安全配置类中替换默认的拦截器


@Override
protected void configure(HttpSecurity http) throws Exception {
    //前后端分离,数据从json中提取
    http.authorizeRequests()
        .anyRequest().authenticated()
        .and()
        .formLogin().loginPage("/loginPages")
        .and()
        .csrf().disable()
        ;
    /**
         * 将我们自定义的filter过滤器替换其中的某个过滤器(filter)
         *loginFilter() ---change---> UsernamePasswordAuthenticationFilter
         */
    http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
}


将我们自定义的filter过滤器替换其中的某个过滤器(filter)


loginFilter() —change—> UsernamePasswordAuthenticationFilter


目录
相关文章
|
29天前
|
前端开发 Java UED
SpringMVC全局异常处理+拦截器使用+参数校验
通过使用 SpringMVC 的全局异常处理、拦截器和参数校验,可以有效提升 Web 应用程序的安全性、稳定性和用户体验。这些技术的合理应用,不仅可以保证代码的健壮性,还能提高代码的可维护性,为开发高质量的 Web 应用程序提供了坚实的基础。
45 6
|
7月前
SpringMVC拦截器的介绍,拦截器的基本实现,拦截器链配置
SpringMVC拦截器的介绍,拦截器的基本实现,拦截器链配置
65 2
|
7月前
过滤器&拦截器
过滤器&拦截器
63 0
过滤器&拦截器
|
7月前
SpringMVC-拦截器参数及拦截器链配置
SpringMVC-拦截器参数及拦截器链配置
67 0
|
Java 容器
过滤器和拦截器的区别
Filter 也称为过滤器,基于Servlet实现,拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行,基于AOP思想,对方法进行增强。和servlet 中的过滤器类似,都是对用户请求进行处理。
84 0
|
SQL 监控 前端开发
Springboot过滤器和拦截器详解及使用场景
过滤器和拦截器触发时机不一样,过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前。
|
Java 应用服务中间件 API
过滤器和拦截器
过滤器和拦截器
173 0
boot 中自定义shiro过滤器的拦截顺序
@boot 中filter SecurityUtils.getSubject()No SecurityManager accessible
362 0
boot 中自定义shiro过滤器的拦截顺序
|
JSON 运维 数据格式
[SpringMVC]拦截器②(拦截器参数、拦截器链配置)
拦截器②(拦截器参数、拦截器链配置)
[SpringMVC]拦截器②(拦截器参数、拦截器链配置)
SpringMVC:拦截器和过滤器的区别
SpringMVC:拦截器和过滤器的区别
125 0
SpringMVC:拦截器和过滤器的区别