SpringSecurity 学习总结

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

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

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

保护的内容就包括 认证 和 受权验证 若是单针对Filter的话 他包括 登录认证,和对URL的权限验证, 登录认证是在SpringSecurity的过滤器链中的AbstractAuthenticationProcessingFilter的子类来作的 具体步骤ide

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

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

因此若是要自定义本身的一套登录认证的话,url

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

咱们须要自定义本身的过滤器 经过 继承 AbstractAuthenticationProcessingFilter 类 实现 attemptAuthentication方法orm

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....)

相关文章
相关标签/搜索