Springboot+Security(二)访问资源时鉴权

在上一篇对用户认证授权之后,用户访问资源的时候,需要获得这个资源的权限和用户的权限,然后通过决策器来比对用户是否有权限访问该资源。一般可以在Security中配置需要的权限,然后使用默认的鉴权即可。这里是使用了自定的鉴权方法和拦截器。

1.鉴权的基本流程

访问资源的时候,即访问url时,会先通过 AbstractSecurityInterceptor 拦截器拦截请求,它会调用 FilterInvocationSecurityMetadataSource 里的方法来获取拦截的url所需要的的全部权限,如果请求的url不在权限表里,那么就会放行,否则会调用决策器 AccessDecisionManager 的决策方法 decide() 来决策用户是否拥有权限访问资源。在 AccessDecisionManager 中它会获得保存在spring的全局缓存SecurityContextHolder 中的用户信息,然后在decide() 方法中比对资源所需权限和用户权限,根据所配策略(有:一票决定,一票否定,少数服从多数等)来决定用户是否有权限访问资源。

2.实现鉴权

2.1 实现FilterInvocationSecurityMetadataSource接口

MyInvocationSecurityMetadataSourceService.java


//鉴权所需要的资源载入  1.先加载权限表中的所有权限  2.查看访问的URL是否在权限表中存在 若存在则调用MyAccessDecisionManager(决策器)
//中的 decide 方法来判断用户是否有该权限
@Service
public class MyInvocationSecurityMetadataSourceService implements
        FilterInvocationSecurityMetadataSource {
    @Autowired
    private PermissionService permissionService;

    private HashMap<String, Collection<ConfigAttribute>> map =null;

    /**
     * 加载权限表中所有权限
     */
    public void loadResourceDefine(){
        map = new HashMap<>();
        Collection<ConfigAttribute> array;
        ConfigAttribute cfg;
        List<Permission> permissions = permissionService.findall();
        for(Permission permission : permissions) {
            array = new ArrayList<>();
            cfg = new SecurityConfig(permission.getName());
            //此处只添加了用户的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
            array.add(cfg);
            //用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
            map.put(permission.getUrl(), array);
        }

    }

    //此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        if(map ==null) loadResourceDefine();
        //object 中包含用户请求的request 信息
        HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
        AntPathRequestMatcher matcher;
        String resUrl;
        for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
            resUrl = iter.next();
            matcher = new AntPathRequestMatcher(resUrl);
            if(matcher.matches(request)) {
                return map.get(resUrl);
            }
        }
        return null;

    }
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }
    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

2.2 实现AccessDecisionManager接口

MyAccessDecisionManager.java(决策器)


@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
    // decide 方法是判定是否拥有权限的决策方法,
    //authentication 是用户信息中权限的集合.
    //object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
    //configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
    //configAttributes资源所需要的权限
    @Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if(null== configAttributes || configAttributes.size() <=0) {
            return;
        }
        ConfigAttribute c;
        String needRole;
        for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
            c = iter.next();
            needRole = c.getAttribute();
            for(GrantedAuthority ga : authentication.getAuthorities()) {
                if(needRole.trim().equals(ga.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("no right");
    }
    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

2.3 继承 AbstractSecurityInterceptor 并且实现Filter接口

MyFilterSecurityInterceptor.java(自定义拦截器)


@Service
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

    @Autowired
    private FilterInvocationSecurityMetadataSource securityMetadataSource;

    @Autowired
    public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
        super.setAccessDecisionManager(myAccessDecisionManager);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
        invoke(fi);
    }
    public void invoke(FilterInvocation fi) throws IOException, ServletException {
    //fi里面有一个被拦截的url
    //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限,如果权限表中有权限,调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够,如果没有就放行,表示可以访问。
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            //执行下一个拦截器
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }
    @Override
    public void destroy() {

    }

    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

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

3.Security资源配置

SecurityConfig.java(配置类)


@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private AuthenticationProvider provider;  //注入我们自己的AuthenticationProvider
    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    @Autowired
    private MyAuthenticationFailHander myAuthenticationFailHander;
    @Autowired
    private MyFilterSecurityInterceptor myFilterSecurityInterceptor;

        @Override
    protected void configure(HttpSecurity http) throws Exception {
            http.
                    authorizeRequests()
                    .anyRequest().authenticated()	
                    .and()
                    .formLogin()
                    .and()
                    .csrf().disable()
                    .formLogin().loginPage("/login").permitAll()
                    .loginProcessingUrl("/form")
                    .successHandler(myAuthenticationSuccessHandler)
                    .failureHandler(myAuthenticationFailHander);
           http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);

        }

    //配置我们自定义的验证授权  让 spring security使用我们自定义的验证器 而不是默认的验证器  也就是MyAuthenticationProvider
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // TODO Auto-generated method stub
        //注册一个自定义的 AuthenticationProvider
        auth.authenticationProvider(provider);
    }

    @Bean
   public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

4.测试

权限表:
在这里插入图片描述
访问localhost:8080 自动跳转到登录页面
在这里插入图片描述
登录普通用户访问 localhost:8080/admin 资源 因为资源需要ROLE_ADMIN权限所以报错403
在这里插入图片描述
而 localhost:8080 页面只需要ROLE_USER 权限 所以能够访问在这里插入图片描述
再登录ADMIN 用户 访问 localhost:8080/admin 访问成功
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值