自定义认证授权过滤器-SpringSecurity

1.SpringSecurity内置认证流程

通过研究SpringSecurity内置基于form表单认证的UsernamePasswordAuthenticationFilter过滤器,我们可以仿照自定义认证过滤器:

内置认证过滤器的核心流程:

 

核心流程梳理如下:

  • 认证过滤器(UsernamePasswordAuthentionFilter)接收form表单提交的账户、密码信息,并封装成UsernamePasswordAuthenticationToken认证凭对象;

  • 认证过滤器调用认证管理器AuthenticationManager进行认证处理;

  • 认证管理器通过调用用户详情服务获取用户详情UserDetails;

  • 认证管理器通过密码匹配器PasswordEncoder进行匹配,如果密码一致,则将用户相关的权限信息一并封装到Authentication认证对象中;

  • 认证过滤器将Authentication认证过滤器放到认证上下文,方便请求从上下文获取认证信息;

2.自定义Security认证过滤器

 UsernamePasswordAuthentionFilter过滤器继承了模板认证过滤器AbstractAuthenticationProcessingFilter抽象类,我们也可仿照实现:

 仿照实现抽象类
package com.itheima.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Optional;

/**
 * ClassName: MyAbstractAuthenticationProcessingFilter
 * Package: com.itheima.security.filter
 * Description:
 *
 * @Author R
 * @Create 2024/2/12 12:08
 * @Version 1.0
 */
public class MyAbstractAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";

    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    /**
     * 自定义构造器 传入登录的地址
     * @param loginUrl
     */
    public MyAbstractAuthenticationProcessingFilter(String loginUrl) {
        super(loginUrl);
    }

    /**
     * 尝试认证
     * @param request from which to extract parameters and perform the authentication
     * @param response the response, which may be needed if the implementation has to do a
     * redirect as part of a multi-stage authentication process (such as OpenID).
     * @return
     * @throws AuthenticationException
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        //不是applicationjson的格式
        if (!request.getMethod().equalsIgnoreCase("POST") && !MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(request.getContentType())) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }

        HashMap<String,String> userInfo = new ObjectMapper().readValue(request.getInputStream(), HashMap.class);

        //获取
        String username = userInfo.get(SPRING_SECURITY_FORM_USERNAME_KEY);
        username = (username != null) ? username : "";
        username = username.trim();
        String password = userInfo.get(SPRING_SECURITY_FORM_PASSWORD_KEY);
        password = (password != null) ? password : "";
        //组长认证票据对象
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        // Allow subclasses to set the "details" property
        //交给认证管理器
        return this.getAuthenticationManager().authenticate(authRequest);
    }
    //认证成功后执行

    /**
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult the object returned from the <tt>attemptAuthentication</tt> 认证权限信息
     * method.
     * @throws IOException
     * @throws ServletException、、
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        User principal = (User) authResult.getPrincipal();
        String username = principal.getUsername();
        Collection<GrantedAuthority> authorities = principal.getAuthorities();
        //响应数据格式
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        //编码格式
        response.setCharacterEncoding("UTF-8");
        HashMap<String, String> info = new HashMap<>();
        info.put("msg","登录成功");
        info.put("data","");
        info.put("code","1");
        //响应
        response.getWriter().write(new ObjectMapper().writeValueAsString(info));
    }
    //认证失败后
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        //响应数据格式
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        //编码格式
        response.setCharacterEncoding("UTF-8");
        HashMap<String, String> info = new HashMap<>();
        info.put("msg","认证失败");
        info.put("data","");
        info.put("code","0");
        //响应
        response.getWriter().write(new ObjectMapper().writeValueAsString(info));
    }
}
定义获取用户详情服务bea
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private TbUserMapper tbUserMapper;
    //根据认证时传入的用户名去指定数据库下获取用户相关的详情信息 用户名 密文密码 权限集合
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        TbUser user = tbUserMapper.findByUserName(username);
        if (user==null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        //构建认证明细对象
        //组装用户详细信息
        List<GrantedAuthority> list = AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles());
        UserDetails build = User.builder().username(user.getUsername())
                .password(user.getPassword())
                .authorities(list).build();
        return build;
    }
}
定义SecurityConfig类

配置默认认证过滤器,保证自定义的认证过滤器要在默认的认证过滤器之前;

@Configuration
@EnableWebSecurity//开启web安全设置生效
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()//定义认证时使用form表单的方式提交数据
                .and()
                .logout()//登出用默认的路径登出 /logout
                .permitAll()//允许所有的用户访问登录或者登出的路径,如果 .anyRequest().authenticated()注释掉,则必须添加permitAll(),否则就不能正常访问登录或者登出的路径
                .and()
                .csrf().disable()
                .authorizeRequests();//授权方法,该方法后有若干子方法进行不同的授权规则处理
        //将自定义的过滤器加入到security过滤器链,且在默认的认证过滤器之前执行
        http.addFilterBefore(myAbstractAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
    }


//    @Bean
//    public PasswordEncoder passwordEncoder() {
//        return new BCryptPasswordEncoder();
//    }

//    @Bean
//    public BCryptPasswordEncoder bCryptPasswordEncoder(){
//        return new BCryptPasswordEncoder();
//    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        log.info("000000000000000000000000000000000000000000000加载");
        return new BCryptPasswordEncoder();
    }
    /**
     * 自定义过滤器  认证成功 上下文中维护认证相关的信息
     * 如果上下文中存在 泽默认的UsernamePasswordAuthentionFilter就不在认证 我们的在前
     * @return
     * @throws Exception
     */
    @Bean
    public MyAbstractAuthenticationProcessingFilter myAbstractAuthenticationProcessingFilter() throws Exception {
        //构造认证过滤器对象 设置认证路径
        MyAbstractAuthenticationProcessingFilter myAbstractAuthenticationProcessingFilter = new MyAbstractAuthenticationProcessingFilter("/myLogin");
        myAbstractAuthenticationProcessingFilter.setAuthenticationManager(authenticationManagerBean());
        return myAbstractAuthenticationProcessingFilter;
    }
}

接口测试

 tips:当查询数据库的数据时 要注意存储的密文加前缀

 

3.执行流程 

因为默认实现的抽象类执行的是表单提交 cookie和session的形式  而对于前后端分离并不适用,因此自定义继承抽象类,以json的形式接受数据 响应数据

组装成凭据对象交给认证管理器

根据认证时传入的用户名去指定数据库下获取用户相关的详情信息 用户名 密文密码 权限集合,构建认证明细对象 组装用户详细信息

 spring底层自动执行match方法

执行成功 回调方法

 

  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Security 是一个用于身份验证和授权的强大架。它提供了一套完整的认证授权流程,以下是 Spring Security认证授权流程: 1. 用户提交登录请求,服务器接收到请求后,进入认证过滤器链。该过滤器链由多个过滤器组成。 2. 过滤器链中的第一个过滤器是 UsernamePasswordAuthenticationFilter,它负责处理基于用户名和密码的认证方式。该过滤器将用户提交的用户名和密码封装到一个 Authentication 对象中。 3. 过滤器链中的下一个过滤器是 AuthenticationManager,它是 Spring Security 的核心接口。AuthenticationManager 负责验证 Authentication 对象的有效性。它会委托给一个或多个 AuthenticationProvider 来进行具体的认证操作。 4. AuthenticationProvider 是 AuthenticationManager 的实现类,它负责具体的认证逻辑。Spring Security 提供了多个内置的 AuthenticationProvider 实现,如 DaoAuthenticationProvider(使用数据库进行认证)、LdapAuthenticationProvider(使用 LDAP 进行认证)等。开发者也可以自定义 AuthenticationProvider。 5. 在认证过程中,AuthenticationProvider 会根据用户提供的凭证(如用户名和密码)进行认证,并生成一个经过认证的 Authentication 对象。 6. 当 AuthenticationProvider 认证成功后,Authentication 对象将被返回给 AuthenticationManager,并最终返回给 UsernamePasswordAuthenticationFilter。 7. 在认证成功后,UsernamePasswordAuthenticationFilter 会生成一个包含用户身份信息的 Authentication 对象,并将其存储在 Spring Security 的上下文中。 8. 接下来,授权过滤器链会根据请求的 URL 和用户的权限信息,决定是否允许用户访问该资源。授权过滤器链由多个过滤器组成,其中最重要的是 AccessDecisionManager 和 AccessDecisionVoter。 9. AccessDecisionManager 是授权决策的核心接口,它负责根据用户的权限信息和资源的访问要求,决定是否允许用户访问该资源。AccessDecisionManager 可以配置多个 AccessDecisionVoter,用于进行具体的授权决策。 10. AccessDecisionVoter 是 AccessDecisionManager 的实现类,它是授权决策的具体实现。Spring Security 提供了多个内置的 AccessDecisionVoter 实现,如 RoleVoter(基于角色的授权)、AuthenticatedVoter(基于认证状态的授权)等。开发者也可以自定义 AccessDecisionVoter。 以上就是 Spring Security认证授权流程。它通过一系列的过滤器和接口来实现用户认证和资源授权的功能,保障了应用程序的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值