security实现账密、手机号和微信三种方式登陆

本文介绍了如何使用Spring Security实现账号密码、手机号和微信三种登录方式。核心功能包括认证、授权和攻击防护。通过配置不同的AuthenticationFilter、AuthenticationProvider以及AuthenticationSuccessHandler和AuthenticationFailureHandler,实现了登录过程的定制。文章详细讲解了每种登录方式的实现原理,并提供了相关配置和代码示例。
摘要由CSDN通过智能技术生成

:security实现多种方式登陆

spring security 的核心功能主要包括
认证
授权
攻击防护
其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。


在说多种认证方式之前,咱们先简单过下单认证方式是如何配置的,也说下Spring Security的各个配置类的作用。

1、UsernamePasswordAuthenticationFilter
Spring Security 默认认证过滤器,处于拦截器链当中,继承AbstractAuthenticationProcessingFilter,咱们看一下源码
在这里插入图片描述
在这里插入图片描述
可以看出里面构造方法指定了默认拦截地址 /login,attemptAuthentication是父类AbstractAuthenticationProcessingFilter抽象方法的实现方法,在父类中doFilter方法里调用,可以看到方法实现是从request里取得用户名密码,最后构建成UsernamePasswordAuthenticationToken,然后调用AuthenticationManager的 authenticate 方法作为参数传进去进行认证。
2、UsernamePasswordAuthenticationToken
UsernamePasswordAuthenticationToken没什么好讲的,在其实就是对认证参数用户名密码的封装,当然后续登录成功之后会作为用户认证信息的封装。
3、AuthenticationManager
authenticationManager是AbstractAuthenticationProcessingFilter的一个成员变量,从上面可以看出,这个参数是必须赋值的,采用默认的过滤器认证,spring security会默认给一个实现类ProviderManager,看下代码
在这里插入图片描述
在这里插入图片描述
从源码看到,管理器会遍历所有注册的认证器集合,调用每个认证器的authenticate认证,此时会有疑惑,如果多个登录方式,肯定会有多个认证器,每次都遍历认证所有的认证器是否不太合理?关键在于以下这个判断代码

这个 toTest 参数就是过滤器传进来的 UsernamePasswordAuthenticationToken

Class<? extends Authentication> toTest = authentication.getClass();
 
if (!provider.supports(toTest)) {
   
     continue;
}

在这里插入图片描述
会调用每个认证器的supports方法,只有此方法返回true,才会执行认证,(由此想到如果自定义认证器,此方法一定要重写),此方法如何实现,咱们看一下此方法的默认实现,会判断

public boolean supports(Class<?> authentication) {
   
        return (UsernamePasswordAuthenticationToken.class
                .isAssignableFrom(authentication));
  }

由此看出,参数必须为 UsernamePasswordAuthenticationToken 类或者其子类的字节码,此参数又是由UsernamePasswordAuthenticationFilter 里传过来的

由此得知,每个过滤器都需要一个AbstractAuthenticationToken的子类绑定
4、AuthenticationProvider
这个是重点配置,具体认证方法都是在这里实现的,因此我们要自定义我们的认证方法,都需要实现这个接口,这个接口只有两个方法,authenticate用来认证,supports 用来决定启用条件
具体实现方法根据自己的业务需要,一般是查询数据库,对比密码,看下我的实现类,一般我们会注入一个自定义的UserDetailService实现类,重写 loadUserByUsername,具体根据用户名查询用户信息,认证成功将用户信息包装成 Authentication 返回
5、AuthenticationSuccessHandler、AuthenticationFailureHandler
看过滤器源码,认证结束后,会根据认证成功或失败,分别调用两个成功失败处理器

successHandler.onAuthenticationSuccess(request, response, authResult);

failureHandler.onAuthenticationFailure(request, response, failed);

因此,我们可以自定义这两个处理器,来自己处理认证成功失败
6、配置文件
最后,Spring Security的配置文件

根据上面的介绍和代码,我们大概可以知道原理了,现在开始撸码吧!

pom文件添加security的依赖
${spring-boot.version} 2.3.1.RELEASE

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

实现UsernamePasswordAuthenticationFilter
该步骤作为过滤器适用,根据判断是否存在对应session,以提示是否需要重新登陆


@Slf4j
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
   
    private static final List<String> forIndexList = Lists.newArrayList();

    static {
   
        forIndexList.add("/");
    }


    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
   
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        //根路径,直接跳转到index页面
        if (forIndexList.contains(request.getServletPath())) {
   
            response.sendRedirect(request.getRequestURL() + "index.html");
            return;
        }
        log.info("===>URL={} , IP={}", request.getRequestURL(), request.getRemoteAddr());

        // 如果是接口请求验证登录情况
        if (!request.getRequestURL().toString().contains(".html")
                && !"/login".equals(request.getServletPath())
                && !"/mobileLogin".equals(request.getServletPath())
                && !"/wxLogin".equals(request.getServletPath())) {
   
            //用户是否登录
            LoginUserDetail currentUser = SessionUtils.getCurrentUser(request);
            log.info("current user {}", JSON.toJSONString(currentUser));
            if (currentUser == null) {
   
                response.setContentType("application/json;charset=UTF-8");
                PrintWriter printWriter = response.getWriter();
                Response httpResponse = new Response(401, "请先登录!");
                printWriter.write(JSON.toJSONString(httpResponse));
                printWriter.flush();
                printWriter.close();
                return;
            }
        }
        chain.doFilter(request, response);
    }
}

实现UserDetails,作为session的信息实体

public class LoginUserDetail implements UserDetails, Serializable {
   

    private Long id;
    private String username;
    private String password;

    public Long getId() {
   
        return id;
    }

    public void setId(Long id) {
   
        this.id = id;
    }

    public void setUsername(String username) {
   
        this.username = username;
    }

    public void setPassword(String password) {
   
        this.password = password;
    }


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
   
        return null;
    }

    @Override
    public String getPassword() {
   
        return password;
    }

    @Override
    public String getUsername() {
   
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
   
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
   
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
   
        return true;
    }

    @Override
    public boolean isEnabled() {
   
        return true;
    }
}

登陆方式1:账号密码登陆

public class UsernameAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
   
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    private boolean postOnly = true;

    public UsernameAuthenticationProcessingFilter() {
   
        super(new AntPathRequestMatcher("/login", "POST"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
   
        if (postOnly && !request.getMethod().equals("POST")) {
   
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值