欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前后端分离 spring security实现多认证功能

程序员文章站 2022-06-13 15:18:58
...

前后端分离 spring security实现多认证功能

spring security简说
spring security大体上是由一堆Filter(所以才能在spring mvc前拦截请求)实现的,Filter有几个,登出Filter(LogoutFilter),用户名密码验证Filter(UsernamePasswordAuthenticationFilter)之类的,Filter再交由其他组件完成细分的功能,例如最常用的UsernamePasswordAuthenticationFilter会持有一个AuthenticationManager引用,AuthenticationManager顾名思义,验证管理器,负责验证的,但AuthenticationManager本身并不做具体的验证工作,AuthenticationManager持有一个AuthenticationProvider集合,AuthenticationProvider才是做验证工作的组件,AuthenticationManager和AuthenticationProvider的工作机制可以大概看一下这两个的java doc,然后成功失败都有相对应该Handler 。大体的spring security的验证工作流程就是这样了。

推介案例
关于spring security实现多认证功能网上有大量的案例
推介一个比较友好的案例:点这儿!!!

实现方式

  1. 未定义多个Filter来去监听各url,因为项目本身就需要多个接口调用在接口内部模仿Filter的调用
  // 该方法会去调用UserDetailsService.loadUserByUsername
 authentication = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(username, password));
  1. 实现AuthenticationProvider接口去实现多个认证模块
public class WeChatAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    @Qualifier(value = "WeChatUserDetailsServiceImpl")
    private UserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    	//authentication 其实就是下面定义的 xxxxToke
        String code = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
        //通过code获取对应的微信open等参数
        
        //调用逻辑
        
        return new WeChatAuthenticationToken(userDetailsService.loadUserByUsername(code);
    }

    @Override
    public boolean supports(Class<?> aClass) {
        /**
         * providerManager会遍历所有
         * securityconfig中注册的provider集合
         * 根据此方法返回true或false来决定由哪个provider
         * 去校验请求过来的authentication
         */
        return (WeChatAuthenticationToken.class.isAssignableFrom(aClass));
    }
}
  1. 编写对应的Token继承AbstractAuthenticationToken方法,可以参考UsernamePasswordAbstractAuthenticationToken实现,下图为自己定义的微信认证
public class WeChatAuthenticationToken extends AbstractAuthenticationToken  {

    private static final long serialVersionUID = 520L;
    private final Object principal;
    private Object credentials;

    public WeChatAuthenticationToken(Object principal) {
        super((Collection)null);
        this.principal = principal;
        this.setAuthenticated(false);
    }

    public WeChatAuthenticationToken(Object principal, Object credentials) {
        super((Collection)null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }

    public WeChatAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }

    public Object getCredentials() {
        return this.credentials;
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        } else {
            super.setAuthenticated(false);
        }
    }

    public void eraseCredentials() {
        super.eraseCredentials();
        this.credentials = null;
    }
}
  1. 实现接口UserDetailsService 并重写对应的loadUserByUsername方法,去实现羡慕的逻辑,一般这儿我习惯写一些链接数据库的操作,而在重写的AbstractAuthenticationToken 类上就写一些参数的基本验证
public class WeChatUserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    @Qualifier(value = "UserDetailsServiceImpl")
    private UserDetailsServiceImpl userDetailsService;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        System.out.println("我进来了!!!!!" + s);
        //验证参数
//        userDetailsService.commonCheckData();
        //返回结果集
//        return userDetailsService.createLoginUser();
        return null;
    }

}

最后只需要在WebSecurityConfigurerAdapter的身份认证接口上把我们重写的provider方法添加上即可

/**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        //替换默认认证用户接口类,并声明解码方式为BCryptPasswordEncoder
        //auth.userDetailsService方法把默认控制器进行替换
        //会被DaoAuthenticationProvider进行调用
        //.passwordEncoder(bCryptPasswordEncoder())替换默认的解密类
//        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
        //账号密码登录
        auth.authenticationProvider(WeChatAuthenticationProvider());
        //微信登录
        auth.authenticationProvider(UsernamePasswordAuthenticationProvider());
    }

注意是AuthenticationManagerBuilder参数的configure方法!!!!!

如果需要多认证就多实现几个provider和token就可以了依葫芦画瓢的事情。
其实本质上spring security并不是很难,需要理解他的调用方式然后一步一步的实现,而且他自身已经有实现类给你参考了实在写不出来你就模仿他提供的类就好了比如UsernamePasswordAbstractAuthenticationToken, DaoAuthenticationProvider,UsernamePasswordAuthenticationFilter等等

相关标签: 学习 java