自定义配置拦截器

简介: 自定义配置拦截器

自定义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


目录
相关文章
|
Java Spring 容器
【Java】Spring如何扫描自定义的注解?
【Java】Spring如何扫描自定义的注解?
360 0
|
存储 缓存 算法
数据结构-链表(一)
链表(Linked List)是一种常见的数据结构,用于存储和组织数据。与数组不同,链表的元素(节点)在内存中不必连续存储,而是通过指针链接在一起。 链表由多个节点组成,每个节点包含两部分:数据(存储实际的元素值)和指针(指向下一个节点的引用)。链表的第一个节点称为头节点,最后一个节点称为尾节点,尾节点的指针通常指向空值(null)。
318 1
|
7月前
|
安全 Java 数据库
Spring Security 实战指南:从入门到精通
本文详细介绍了Spring Security在Java Web项目中的应用,涵盖登录、权限控制与安全防护等功能。通过Filter Chain过滤器链实现请求拦截与认证授权,核心组件包括AuthenticationProvider和UserDetailsService,负责用户信息加载与密码验证。文章还解析了项目结构,如SecurityConfig配置类、User实体类及自定义登录逻辑,并探讨了Method-Level Security、CSRF防护、Remember-Me等进阶功能。最后总结了Spring Security的核心机制与常见配置,帮助开发者构建健壮的安全系统。
387 0
|
3月前
|
SQL 数据采集 数据处理
终于有人把数据架构讲清楚了!
本文深入浅出地解析了数据架构的核心逻辑,涵盖其定义、作用、设计方法及常见误区,助力读者构建贴合业务的数据架构。
|
Java 数据安全/隐私保护
Java使用PDFBox开发包实现对PDF文档内容编辑与保存
Java使用PDFBox开发包实现对PDF文档内容编辑与保存
488 7
Exception in thread "main" java.lang.IllegalArgumentException: U+6570 ('.notdef') is not available in the font Helvetica-Bold, encoding: WinAnsiEncoding 问题解决
【5月更文挑战第26天】Exception in thread "main" java.lang.IllegalArgumentException: U+6570 ('.notdef') is not available in the font Helvetica-Bold, encoding: WinAnsiEncoding 问题解决
923 2
|
Oracle Java 关系型数据库
SpringBoot整合Mybatis连接Oracle数据库
SpringBoot整合Mybatis连接Oracle数据库
SpringBoot整合Mybatis连接Oracle数据库
|
安全 Java 数据库
Spring Security自定义登录认证
Spring Security自定义登录认证
583 0
|
Java
SpringBoot中如何在过滤器中取post的参数值
SpringBoot中如何在过滤器中取post的参数值
854 0
|
JSON Java 数据格式
Spring Boot @ControllerAdvice 处理全局异常,返回固定格式Json
参考文章: Spring Boot @ControllerAdvice 处理全局异常,返回固定格式Json 解决spring boot中rest接口404,500等错误返回统一的json格式 spring boot最新教程(五):404错误500错误统...
3367 0
下一篇
开通oss服务