SpringSecurity 学习总结

馋奶兔 提交于 2020-03-08 12:02:08

一些基础的知识可以参考以下连接:https://www.cnkirito.moe/spring-security-1/  讲真,这老哥写的很好,不过还有很多估计没写完,遗憾

本文章主要是一个大概的总结。

首先SpringSecurity是一个安全框架,它所做的事儿简单概括就是使用Filter对整个项目进行一个保护,

保护的内容就包括 认证 和 授权验证 如果单针对Filter的话 他包括 登陆认证,和对URL的权限验证, 登陆认证是在SpringSecurity的过滤器链中的AbstractAuthenticationProcessingFilter的子类来做的 具体步骤

请求进来->若干前置过滤器->AbstractAuthenticationProcessingFilter.doFilter()-> AbstractAuthenticationProcessingFilter.attemptAuthentication()->如果获取到了Authentication则继续往下执行,否则重定向到登陆页面.... 从attemptAuthentication方法中的认证操作是调用了AuthenticationManager的authenticate方法来进行登陆认证,然后会把用户提交的用户信息封装成AuthenticationToken的实例丢进去,在其中进行验证,验证成功则返回填充了相关信息的Token对象,否则是null,则说明认证失败,会重定向到我们设置好的loginUrl。

这个认证过程中还涉及到 AuthenticationManager 的认证 是 AuthenticationManager的一个实现类ProviderManager,而ProviderManager会有一个List专门存放Provider,这些Provider都要实现AuthenticationProvider接口,只要有一个Provider支持放进来的Authentication则会由这个Provider进行处理,且这个方法可以抛出异常,抛出也说明登陆失败。

所以如果要自定义自己的一套登陆认证的话,

我们涉及到三个地方 Filter - Provider - Token (例子采用上面链接的老哥的例子)

我们需要自定义自己的过滤器 通过 继承 AbstractAuthenticationProcessingFilter 类 实现 attemptAuthentication方法

public class IpAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

    public IpAuthenticationProcessingFilter(String uri) {
        super(new AntPathRequestMatcher(uri));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
        //获取host信息
        String host = httpServletRequest.getRemoteHost();
        //交给内部的AuthenticationManager去认证,实现解耦
        return getAuthenticationManager().authenticate(new IpAuthenticationToken(host));
    }
}

我们需要自定义自己的认证提供器 通过继承 AuthenticationProvider 类

public class IpAuthenticationProvider implements AuthenticationProvider {
    final static Map<String, SimpleGrantedAuthority> ipAuthorityMap = new ConcurrentHashMap<>();
    //维护一个ip白名单列表,每个ip对应一定的权限
    static {
        ipAuthorityMap.put("127.0.0.1", new SimpleGrantedAuthority("ADMIN"));
        ipAuthorityMap.put("10.0.0.8", new SimpleGrantedAuthority("FRIEND"));
    }
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        if (authentication instanceof IpAuthenticationToken){
            String ip = ((IpAuthenticationToken)authentication).getIp();
            if (ipAuthorityMap.containsKey(ip)){
                HashSet<SimpleGrantedAuthority> grants = new HashSet();
                grants.add(ipAuthorityMap.get(ip));
                return new IpAuthenticationToken(ip,grants);
            }
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return IpAuthenticationToken.class.isAssignableFrom(aClass);
    }
}

我们需要自定义自己的 认证对象 一般就是我们的领域对象 但是这个对象必须实现 Authentication 接口 我们可以直接继AbstractAuthenticationToken类即可

public class IpAuthenticationToken extends AbstractAuthenticationToken {
    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    private String ip;

    public IpAuthenticationToken(String ip) {
        super(null);
        this.ip = ip;
        super.setAuthenticated(false);
    }

    public IpAuthenticationToken(String ip, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.ip = ip;
        super.setAuthenticated(true);
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return null;
    }
}

然后我们需要将provider添加到ProviderManager的List中去

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(new IpAuthenticationProvider());
    auth.userDetailsService(userDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance());
}

 将我们自定义的Filter注册到过滤器链中去

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/iplogin","/login").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
        .loginPage("/iplogin");
    //这个操作就是把我们自定义的过滤器插入到UsernamePasswordAuthenticationFilter的前面,表示优先使用我们的验证,如果验证成功则会将Authentication对象放入                    //SecurityContext中,后面的过滤器在进行验证时会检测SecurityContext中是否有相应的对象,没有则会
    http.addFilterBefore(ipAuthenticationProcessingFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
}

搞定 这个时候我们的登陆验证方式已经改成了iplogin(虽然原始的login还在,但是由于我们设置了loginPage的url为/iplogin所以那个页面在登陆失败的时候永远也访问不到,除非你直接访问/login....)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!