spring sercurity之鉴权

github的项目源码,建议 导入项目后,运行 然后看着博客学习。
document\urp.sql 数据库 sql

能力提升

博客只是能把你领进门,有很多特性我没有讲,感兴趣的 可以看 官方 文档
spring sercurity官方文档
如果只是满足于怎么样spring sercurity博客就可以,如果想各种定制那么还是官方文档和源码吧。


在上一篇博客登录用户,我们需要对资源 url进行保护,这个就用到了鉴权。
我个人理解,spring sercurity 的鉴权的类 主要有这两个类。当然 我们也可以自定义类来完成相同的功能。

  1. FilterSecurityInterceptor 这个类和我们配置文件的authorizeRequests息息相关,例如 那个url 需要登录,哪个url 不需要登录 就可以访问。
  2. MethodSecurityInterceptor方法层面的权限控制,和注解一起使用的,例如@PreAuthorize

这两个一个是处理 认证的,也就是 你登录了没有,一个是 你看你这个用户 有没有权限能用这个。 这时候 也需要 有人会问,如果没登录 就到MethodSecurityInterceptor 会怎么样?
这里我很明确的回答一下。如果 一个url你permitAll了,在controller方法哪里 又使用了注解@PreAuthorize。那么你访问这个url 还是提示没有权限的。 因为permitAll()会默认的生成一个匿名用户,@PreAuthorize进行权限鉴权时候,是对匿名用户鉴权的。


AbstractSecurityInterceptor是FilterSecurityInterceptor 和MethodSecurityInterceptor的共同父类。重写他的beforeInvocation方法就可以实现 自定义的权限鉴权业务

鉴权流程

spring sercurity之鉴权_第1张图片
1. 查找与当前请求关联的“configuration attributes”
2. 提交 secure object(安全对象),Authentication ,configuration attributes,给AccessDecisionManager 的decide方法 进行认证。在FilterSecurityInterceptor中,decide的参数object类型为FilterInvocation,MethodSecurityInterceptor中object类型为MethodInvocation,具体类型是ReflectiveMethodInvocation
3. 可选的 ,在发生调用的 改变 Authentication
4. 假设已授予访问权限,允许继续对安全对象调用,就是可以访问这个安全对象
5. 如果配置了 AfterInvocationManager 就调用它, 如果 上面出现异常 就不会调用它。



流程 进入SecurityInterceptor后,首先走 它的doFilter方法,包装一下request ,response。调用invoke()调用beforeInvocation方法调用SecurityMetadataSource获取ConfigAttribute集合丛SecurityContext里面取出Authentication认证对象,如果取出的Authentication没有认证过,那就调用authenticationManager再认证一下调用accessDecisionManager的decide方法 进行权限鉴权。抛异常的就是不允许访问,没有异常的 就是通过的。→调用RunAsManager为当前的current secure object创建一个临时的对象。→beforeInvocation返回一个InterceptorStatusToken尝试再调用FilterChain进行过滤→调用finallyInvocation设置SecurityContext→最后调用afterInvocation,如果有afterInvocationManager 则再进行一次鉴权过称。结束

FilterSecurityInterceptor

没有什么好讲的。反正 只要知道流程了,这个类我们不能替换调用,只能替换掉这个类的内部引用。或者 在Filter Chain 哪里 加入 我们自定义的类。也就是 自定义的类和FilterSecurityInterceptor 共存。如下如所示
spring sercurity之鉴权_第2张图片
我们可以在Filter Chain 添加自定义的类 FilterSecurityInterceptor 也一直会存在 。
在配置文件里面 配置如下

package com.example.sercurity.config;

import com.example.sercurity.component.MyFilter;
import com.example.sercurity.component.RestAuthenticationEntryPoint;
import com.example.sercurity.component.RestfulAccessDeniedHandler;
import com.example.sercurity.config.sercurity.*;
import com.example.sercurity.service.PermissonService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 * 配置token登录的
 *
 * @Author: plani
 * 创建时间: 2019/8/16 17:50
 */
@Configuration
@EnableWebSecurity
//启用 方法鉴权
//下面这个注解 ,项目只能有一个类可以使用,要注意
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
@Order(3)
public class TokenSecurityConfig extends WebSecurityConfigurerAdapter {
    private Logger logger = LoggerFactory.getLogger(FormSecurityConfig.class);
    @Autowired
    private PermissonService permissonService;

    @Autowired
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    private MyAuthticationProvider myAuthticationProvider;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {

        httpSecurity.csrf().disable()//跨域攻击去除
                .sessionManagement()// 基于token,所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()//返回SecurityBuilder
                .authorizeRequests()//配置url
                //这里应该考虑 放开 你的注册接口, 登录接口默认是 /login
                //这里的antMatchers  是在 Filter Chain 最后的Filter起效果。所以 不用放开 /login
                .antMatchers("/register")
                .permitAll()////允许所有人访问
                .anyRequest()//所有请求
                .authenticated()//授权的用户

                //引入了ObjectPostProcessor的概念,可以使用它修改或替换Java配置创建的许多对象实例
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        //设置权限来源  默认的实现类ExpressionBasedFilterInvocationSecurityMetadataSource
                        object.setSecurityMetadataSource(new MySecurityMetadataSource());
                        //设置自定义的 权限 管理器  这里 图方便  直接new了  默认的实现类AffirmativeBased
                        object.setAccessDecisionManager(new MyAccessDecisionManager());
                        return object;
                    }
                })
        ;

        httpSecurity
                //Filter Chain 里面的UsernamePasswordAuthenticationFilter类的位置  添加  我们自定义的Filter
                //在指定Filter类的位置添加筛选器,要注意 位置,这个不要求是Sercurity的 Filter 实例
                .addFilterAt(jwtLoginFilter(), UsernamePasswordAuthenticationFilter.class)


                //在 UsernamePasswordAuthenticationFilter 的位置前面加入 Filter
//                .addFilterBefore(jwtLoginFilter(), UsernamePasswordAuthenticationFilter.class)

                //添加一个Filter,该Filter必须是安全框架中提供的Filter的实例或扩展其中一个Filter。该方法确保自动处理Filter的排序。
//                .addFilter(jwtLoginFilter())

                .addFilterAt(jwtAuthenticationFilter(), BasicAuthenticationFilter.class)
                //这个 MyFilter
                .addFilterAt(new MyFilter(), LogoutFilter.class)
                //也可以在这里 设置 自定义的 securityInterceptor
                //,FilterSecurityInterceptor 依然会存在
                .addFilterAfter(myFilterSecurityInterceptor(), FilterSecurityInterceptor.class)
        ;

        //配置 全局异常 处理方案
        httpSecurity.exceptionHandling()
                //没有权限访问 时候  返回信息
                .accessDeniedHandler(restfulAccessDeniedHandler)
                //当未登录或者token失效访问接口时,自定义的返回结果
                .authenticationEntryPoint(restAuthenticationEntryPoint);

    }

    /**
     * 在这里 配置  WebSecurity的一些东西
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        //设置使用自己的  securityInterceptor
//        web.securityInterceptor(myFilterSecurityInterceptor())
        //忽略 one 路径
        web.ignoring().mvcMatchers("/one");
      
        super.configure(web);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth    // 设置UserDetailsService
                .userDetailsService(userDetailsService)
                // 使用BCrypt进行密码的hash
                .passwordEncoder(passwordEncoder);
//                .and()
        //使用 自己的 authenticationProvider  进行具体的认证过称
//                .authenticationProvider(myAuthticationProvider);
    }


    @Bean
    //这种用 @Bean的 并不是 唯一的,如果 你想 ,你可以直接在上面使用的地方  new一个,不过 类里面的一些其他组件 就没法注入了
    public JWTAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        return new JWTAuthenticationFilter(authenticationManager());
    }

    @Bean
    public JWTLoginFilter jwtLoginFilter() throws Exception {
        JWTLoginFilter jwtLoginFilter = new JWTLoginFilter();
        jwtLoginFilter.setAuthenticationManager(authenticationManagerBean());
        return jwtLoginFilter;
    }

    @Bean
    //这个不能替换 FilterSecurityInterceptor ,即这个和FilterSecurityInterceptor是可以共存的
    public MyFilterSecurityInterceptor myFilterSecurityInterceptor() {
        MyFilterSecurityInterceptor myFilterSecurityInterceptor = new MyFilterSecurityInterceptor();
        //设置自定义的 权限 管理器  这里 图方便  直接new了  默认的实现类AffirmativeBased
        myFilterSecurityInterceptor.setAccessDecisionManager(new MyAccessDecisionManager());
        //设置权限来源  默认的实现类ExpressionBasedFilterInvocationSecurityMetadataSource
        myFilterSecurityInterceptor.setSecurityMetadataSource(new MySecurityMetadataSource());
        return myFilterSecurityInterceptor;
    }
}

值得注意的就是 withObjectPostProcessor 和 .addFilterAfter(myFilterSecurityInterceptor(), FilterSecurityInterceptor.class)。 其他的我注释写的很明白。

MyAccessDecisionManager: 执行具体 鉴权过称的自定义类,业务逻辑写这里就行

package com.example.sercurity.config.sercurity;

import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;

import java.util.Collection;

/**
 * 执行具体 鉴权过称的自定义类
 */
public class MyAccessDecisionManager implements AccessDecisionManager {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * @param authentication   认证对象 就是前面认证过称  返回了Authentication, 如果你返回的是自定义的 ,那么这里的具体类型就是你自定义的类
     * @param object           spring sercurity使用cglib 代理的controller类的方法 返回的对象
     *                         在FilterSecurityInterceptor中,decide的参数object类型为FilterInvocation,
     *                         MethodSecurityInterceptor中object类型为MethodInvocation,具体类型是ReflectiveMethodInvocation
     *                         如果你自定义的话,这里的object类型 可以变成 你自己定义的类型
     * @param configAttributes SecurityMetadataSource返回的 关于这个object的权限集合
     * @throws AccessDeniedException
     * @throws InsufficientAuthenticationException
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if (object instanceof FilterInvocation) {
            logger.info("在FilterSecurityInterceptor 类型调用");
        } else if (object instanceof MethodInvocation) {
            logger.info("MethodSecurityInterceptor 类型调用");
        } else {
            logger.info("自定义的类型调用");
        }

        //写你自己的鉴权 逻辑 ,不通过的话,直接抛异常就行 AccessDeniedException

    }

    /**
     * 指示此AccessDecisionManager是否能够处理使用传递的ConfigAttribute表示的授权请求。
     *
     * @param attribute
     * @return
     */
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    /**
     * 指示AccessDecisionManager实现是否能够为指定的受保护对象类型提供访问控制决策。
     *
     * @param clazz 就是
     * @return
     */
    @Override
    public boolean supports(Class<?> clazz) {
        //FilterInvocation  MethodInvocation
        logger.info("MyAccessDecisionManager " + clazz.getSimpleName());
        return true;
    }
}

MySecurityMetadataSource : 根据request提供具体权限集合的自定义类

package com.example.sercurity.config.sercurity;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * 根据request提供具体权限集合的自定义类
 */
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource, SecurityMetadataSource {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 这一部分 我没有写 具体的代码,因为 至少公司项目层面没用到,这里 我只说一下用法
     * 最好参照 默认实现类的代码  很棒
     *
     * @param object 就是controller 层的类及其方法,和 静态资源等
     * @return
     * @throws IllegalArgumentException
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        //在这里 根据 object 返回 ,返回所要求的 权限集合
        //默认实现类的代码  ,我直接拷过来的。  RequestMatcher 就是 我们前面配置文件  里面定义的 authorizeRequests 哪里的url

      /*  final HttpServletRequest request = ((FilterInvocation) object).getRequest();
        for (Map.Entry> entry : requestMap
                .entrySet()) {
            if (entry.getKey().matches(request)) {
                return entry.getValue();
            }
        }*/
        //这里 我随便 返回一两个
        Set<ConfigAttribute> allAttributes = new HashSet<>();
        allAttributes.add(new ConfigAttribute() {
            @Override
            public String getAttribute() {
                return "custom";
            }
        });
        logger.info("返回 随便定义的  ConfigAttribute 集合 ");
        return allAttributes;
    }

    /**
     * @return 返回全部 ConfigAttribute
     */
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        //FilterInvocation  MethodInvocation
        logger.info("MySecurityMetadataSource " + clazz.getSimpleName());
        return true;
    }
}

MyFilterSecurityInterceptor 这里要关注 他的 父类

package com.example.sercurity.config.sercurity;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.*;
import java.io.IOException;

//继承AbstractSecurityInterceptor  还有实现 Filter接口
//当然 也可以继承具体的实现类  FilterSecurityInterceptor
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private SecurityMetadataSource securityMetadataSource;

    public SecurityMetadataSource getSecurityMetadataSource() {
        return securityMetadataSource;
    }

    public void setSecurityMetadataSource(SecurityMetadataSource securityMetadataSource) {
        this.securityMetadataSource = securityMetadataSource;
    }

    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        logger.info("invoke");
        if (fi.getRequest() != null) {
            // filter already applied to this request and user wants us to observe
            // once-per-request handling, so don't re-do security checking
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } else {
            InterceptorStatusToken token = super.beforeInvocation(fi);
            try {
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            } finally {
                super.finallyInvocation(token);
            }
            super.afterInvocation(token, null);
        }

    }

    //重写AbstractSecurityInterceptor的这个方法
    //在FilterSecurityInterceptor 中, beforeInvocation的参数 object类型 为FilterInvocation,
    //这个要和MethodSecurityInterceptor 的MethodInvocation ,具体类型是ReflectiveMethodInvocation
    @Override
    protected InterceptorStatusToken beforeInvocation(Object object) {
        //object 类型是FilterInvocation 就是上面的fi 参数,可以获得 request  response  url等
        logger.info("这里 我没有改动任何东西 感觉默认的就能实现我的功能,有需要的改");
        return super.beforeInvocation(object);
    }

    @Override
    public Class<?> getSecureObjectClass() {
        //这里的 Class 类型 就是beforeInvocation 参数 object的类型
        return FilterInvocation.class;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return securityMetadataSource;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher("/path");
        //判断 是否匹配  没有作用,只是写着玩的
        antPathRequestMatcher.matches(fi.getRequest());
        invoke(fi);
    }
}

关注一下 这两行代码 我个人觉得 有用,虽然 业务中没用到,判断 request的url 符不符合的

        AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher("/path");
        //判断 是否匹配  没有作用,只是写着玩的
        antPathRequestMatcher.matches(fi.getRequest());

MethodSecurityInterceptor

MethodSecurityInterceptor并不在 Filter Chain中,它是spring sercurity 使用cglib动态代理来的,在 配置文件上面 使用注解 ,来启用它

//启用 方法鉴权
//下面这个注解 ,项目只能有一个类可以使用,要注意
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
@Order(3)
public class TokenSecurityConfig extends WebSecurityConfigurerAdapter

@EnableGlobalMethodSecurity有三个参数,分别是 启用@prePostEnabled 注解,启用@securedEnabled ,启用@jsr250Enabled 注解。关于这三个注解的使用 如下,简略提一下

JSR-250注解
需要在
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
这里提一下 使用 角色鉴权的时候,必须要 前面加ROLE_  ,SimpleGrantedAuthority这里也需要加才行。
//允许  admin 或者 normal用户访问 注意 是个数组
@RolesAllowed({"ROLE_admin","ROLE_normal"})
//允许所有用户访问
@PermitAll
//禁止所有用户访问
@DenyAll
@Secured注解
需要在
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
//允许  admin 或者 normal用户访问 注意 是个数组
@Secured({"ROLE_admin","ROLE_normal"})//前面 角色前面必须要加ROLE

prePostEnabled 注解
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
表达式主要有
这里提一下 使用 角色鉴权的时候,必须要 前面加ROLE_ ,SimpleGrantedAuthority这里也需要加才行。

spring sercurity之鉴权_第3张图片
还有连接词 or and

   //有 admin 权限 才可以访问  或者 有 root角色 就可以
   @PreAuthorize("hasAuthority('admin') or hasAnyRole('ROLE_root')")

MethodSecurityInterceptor也没法自定义,除非你继承AbstractSecurityInterceptor 写个类似的。 但是,你可以定制它,更换它的AccessDecisionManager 为自己的,更换MethodSecurityMetadataSource为自己的。只需要写一个类,继承GlobalMethodSecurityConfiguration 并在这个类里面 重写方法 就可以。note 要在类上面加注解@EnableGlobalMethodSecurity


package com.example.sercurity.config;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.method.MethodSecurityMetadataSource;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;

//下面这个注解 ,项目只能有一个类可以使用,要注意
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class MyGlobalMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {


    /**
     * 设置 AccessDecisionManager  具体的权限鉴权  自己的定义的类 ,如果你想定制化
     *
     * @return
     */
    @Override
    protected AccessDecisionManager accessDecisionManager() {
        return super.accessDecisionManager();
    }

    /**
     * 权限 数据来源  返回你自定义的类,如果 你想设置 MethodSecurityMetadataSource
     *
     * @return
     */
    @Override
    protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
        return super.customMethodSecurityMetadataSource();
    }

}

只需要 在项目中 加一个这样的类,就可以定制化MethodSecurityInterceptorEnableGlobalMethodSecurity注解 ,项目只能有一个类可以使用,要注意

杂项

官方文档 部分节选,关于多个HttpSecurity 和对某些URL 应用设置

Multiple HttpSecurity

我们可以配置多个HttpSecurity实例,就像我们可以有多个块一样。关键是要多次扩展WebSecurityConfigurationAdapter。例如,下面是一个以/api/开头的URL的不同配置示例。

@EnableWebSecurity
public class MultiHttpSecurityConfig {
    @Bean                                                             1
    public UserDetailsService userDetailsService() throws Exception {
        // ensure the passwords are encoded properly
        UserBuilder users = User.withDefaultPasswordEncoder();
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(users.username("user").password("password").roles("USER").build());
        manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build());
        return manager;
    }

    @Configuration
    @Order(1)                                                        2
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http
                .antMatcher("/api/**")                               3
                .authorizeRequests()
                    .anyRequest().hasRole("ADMIN")
                    .and()
                .httpBasic();
        }
    }

    @Configuration                                                   4
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                .formLogin();
        }
    }
}
  1. 正常配置身份验证
  2. 创建一个包含@Order的WebSecurityConfigurerAdapter实例,以指定应该首先考虑哪个WebSecurityConfigurerAdapter。
  3. http.antMatcher声明此HttpSecurity只适用于以/api/开头的url
  4. 创建另一个WebSecurityConfigurerAdapter实例。如果URL不以/api/开头,将使用此配置。这个配置是在ApiWebSecurityConfigurationAdapter之后考虑的,因为它在1之后有一个@Order值(没有@Order缺省值是last)。

ur匹配

这是 spring sercurity 自带的 匹配 url 感觉很好

        AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher("/path");
        //判断 是否匹配  没有作用,只是写着玩的
        antPathRequestMatcher.matches(fi.getRequest());

requestMatchers

requestMatchers() 可以起到和上面 Multiple HttpSecurity 相同的功能

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        //requestMatchers 允许指定将在哪个HttpServletRequest实例上调用此HttpSecurity
        //和 多
        //就是 当访问 /re/** 这些接口的时候  是使用下面 一部分代码的,链式调用 结束 为 分隔符
        httpSecurity.requestMatchers().antMatchers("/re/**")
                .and()
                .requestMatchers()
                .antMatchers("/re/s")//只要链式 没有结束, 那么这一整个配置 就是一体的
                .and()
                //所有人 都可以访问
                .authorizeRequests().anyRequest().permitAll();


        //其他的url  使用下面的这部分配置
        httpSecurity.csrf().disable()//跨域攻击去除
                .sessionManagement()// 基于token,所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()//返回SecurityBuilder
                .authorizeRequests()//配置url
                //这里应该考虑 放开 你的注册接口, 登录接口默认是 /login
                //这里的antMatchers  是在 Filter Chain 最后的Filter起效果。所以 不用放开 /login
                .antMatchers("/register")
                .permitAll()////允许所有人访问
                .anyRequest()//所有请求
                .authenticated()//授权的用户

                //引入了ObjectPostProcessor的概念,可以使用它修改或替换Java配置创建的许多对象实例
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        //设置权限来源  默认的实现类ExpressionBasedFilterInvocationSecurityMetadataSource
                        object.setSecurityMetadataSource(new MySecurityMetadataSource());
                        //设置自定义的 权限 管理器  这里 图方便  直接new了  默认的实现类AffirmativeBased
                        object.setAccessDecisionManager(new MyAccessDecisionManager());
                        return object;
                    }
                })
        ;

        httpSecurity
                //Filter Chain 里面的UsernamePasswordAuthenticationFilter类的位置  添加  我们自定义的Filter
                //在指定Filter类的位置添加筛选器,要注意 位置,这个不要求是Sercurity的 Filter 实例
                .addFilterAt(jwtLoginFilter(), UsernamePasswordAuthenticationFilter.class)


                //在 UsernamePasswordAuthenticationFilter 的位置前面加入 Filter
//                .addFilterBefore(jwtLoginFilter(), UsernamePasswordAuthenticationFilter.class)

                //添加一个Filter,该Filter必须是安全框架中提供的Filter的实例或扩展其中一个Filter。该方法确保自动处理Filter的排序。
//                .addFilter(jwtLoginFilter())

                .addFilterAt(jwtAuthenticationFilter(), BasicAuthenticationFilter.class)
                //这个 MyFilter
                .addFilterAt(new MyFilter(), LogoutFilter.class)
                //也可以在这里 设置 自定义的 securityInterceptor
                //,FilterSecurityInterceptor 依然会存在
                .addFilterAfter(myFilterSecurityInterceptor(), FilterSecurityInterceptor.class)
        ;

        //配置 全局异常 处理方案
        httpSecurity.exceptionHandling()
                //没有权限访问 时候  返回信息
                .accessDeniedHandler(restfulAccessDeniedHandler)
                //当未登录或者token失效访问接口时,自定义的返回结果
                .authenticationEntryPoint(restAuthenticationEntryPoint);

    }

你可能感兴趣的:(java开发)