zxpnet网站 zxpnet网站
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

zxpnet

一个爱学习的java开发攻城狮
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 大后端课程视频归档
  • 南航面试题
  • 并发编程

  • 性能调优

  • java8语法

  • lombok

  • 日志

  • 工具类

  • spring

  • mybatis

  • springboot

  • redis

  • zookeeper

  • springcloud

  • dubbo

  • netty

  • springsecurity

    • springsecurity基本使用及个性化登录配置
    • 微服务安全与实战

    • springsecurity认证流程分析
    • springsecurity过滤器链分析
    • springsecurity图形验证码
    • springsecurity记住我RememberMe功能
    • springsecurity手机验证码登陆
    • springsecurity httpFirewall
    • springsecurity漏洞保护
    • Untitled
    • 跨域问题
    • Springsecurity Session会话管理
    • springsecurity控制授权
      • 控制权限与用户体验
      • 简单权限控制与rbac权限控制
        • 业务系统简单权限控制
      • 1、spring security 过滤器链
      • 2、控制授权的相关类
      • 小结一下鉴权流程
      • 3.配置简单的权限
      • 4.权限表达式
      • 5.spring security控制授权代码封装
      • 6、rbac权限
      • 7、注解式授权
    • 权限模型
    • springseuciryt oauth2基础
    • 基于JWT实现SSO单点登录
    • springsocial第三方登陆
    • springsecurity退出登陆
    • Spring security 集成 JustAuth 实现第三方授权登录
    • Spring Boot+CAS 单点登录
    • Springsecurity常见错误
  • mq消息中间件

  • shiro

  • beetle

  • 模板引擎

  • jpa

  • 数据结构与算法

  • 数据库知识与设计

  • gradle

  • maven

  • bus

  • 定时任务

  • docker

  • centos

  • 加解密

  • biz业务

  • pigx项目

  • 开源项目

  • 品达通用权限项目-黑马

  • 货币交易项目coin-尚学堂

  • php

  • backend
  • springsecurity
shollin
2021-07-04
目录

springsecurity控制授权

  • 控制权限与用户体验
  • 简单权限控制与rbac权限控制
    • 业务系统简单权限控制
  • 1、spring security 过滤器链
  • 2、控制授权的相关类
  • 小结一下鉴权流程
  • 3.配置简单的权限
  • 4.权限表达式
  • 5.spring security控制授权代码封装
  • 6、rbac权限
  • 7、注解式授权

# 控制权限与用户体验

image-20210718165058905

# 简单权限控制与rbac权限控制

一般公司应用有给顾客用的业务系统和公司内部管理的系统

image-20210718170537351

# 业务系统简单权限控制

http.authorizeRequests()
                .antMatchers("/login").permitAll()
                .antMatchers(HttpMethod.GET,"/admin/**").hasRole("ADMIN") // 对应角色:ROLE_ADMIN
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated(); // 其它的请求需要认证
1
2
3
4
5

# 1、spring security 过滤器链

spring security中的除了用户登录校验相关的过滤器,最后还包含了鉴权功能的过滤器,还有匿名资源访问的过滤器链,相关的图解如下: img (opens new window)

FilterSecurityInterceptor

# 2、控制授权的相关类

这里是整个spring security的过滤器链中的授权流程中控制权限的类的相关图示:

img (opens new window)

这里主要是从AccessDecisionVoter的投票者(译称)把信息传递给投票管理者AccessDecisionManager,最终来判断是过还是不过(也就是有没有权限).有两种可能的类:

1.不管有多少请求投票不过,只要有一个过就可以通过(UnanimousBased);

2.不管有多少请求投票通过,只要有一个不通过就不让通过(AffirmativeBased);

3.比较投通过和不通过的个数,谁多久就按照谁的方式来(Consensusbased).

这里可以可能听起来有点绕,但实际上就是三种控制权限的方式类,我们可以认为Spring security已经帮我们做好了最终的判断,我们只需要当一个旁观者即可.

我们再来关注SecurityContextHolder这个类,他会将我们的权限信息封装到Authentication中,SecurityConfig则是相关的Spring security的配置信息,这个类会将相关的信息传递到ConfigAttribute中.

AbstractSecurityInterceptor 核心鉴权方法(精简版)

protected InterceptorStatusToken beforeInvocation(Object object) {

   // 第一步:通过访问请求,找到匹配的权限配置
   Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);

   // 第二步:从SecurityContextHolder里面拿用户信息Authentication,至少会有一个AnonymousAuthenticationToken
   Authentication authenticated = authenticateIfRequired();

   //attemptAuthorization(object, attributes, authenticated);
   
   try {
   		// 第三步:accessDecisionManager做决策,默认策略:有一个投票器通过,就鉴权通过
		this.accessDecisionManager.decide(authenticated, object, attributes);
	}
	catch (AccessDeniedException ex) {
		// 发布事件,被ExceptionTranslationFilter去处理
		publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex));
		throw ex;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 对AccessDeniedException进行处理
private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response,
      FilterChain chain, AccessDeniedException exception) throws ServletException, IOException {
   Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
   boolean isAnonymous = this.authenticationTrustResolver.isAnonymous(authentication);
   if (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) {
      if (logger.isTraceEnabled()) {
         logger.trace(LogMessage.format("Sending %s to authentication entry point since access is denied",
               authentication), exception);
      }
      // 匿名用户的话,则先保存请求信息,再用authenticationEntryPoint去处理
      //this.requestCache.saveRequest(request, response);
	 //	this.authenticationEntryPoint.commence(request, response, reason);
      sendStartAuthentication(request, response, chain,
            new InsufficientAuthenticationException(
                  this.messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication",
                        "Full authentication is required to access this resource")));  	 
  }
   else {
		// 已登陆用户,则用accessDeniedHandler去处理
      this.accessDeniedHandler.handle(request, response, exception);
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

image-20210718173739394

image-20210718175147025

image-20210718180304555

image-20210718181211641

image-20210718182420148

# 小结一下鉴权流程

1、根据请求,从security配置的权限里面,找出ConfigAttribute集合,如WebExpressionConfigAttribute (#oauth2.throwOnError(hasRole('ROLE_USER')))

2、从SecurityContextHolder里面拿登陆用户信息Authentication,至少会有一个AnonymousAuthenticationToken

3、第三步:accessDecisionManager做决策,默认策略:有一个投票器通过,就鉴权通过。如果不通过,则抛AccessDeniedException异常

4、第四步:ExceptionTranslationFilter接收到异常,如果是匿名用户的话,则先保存请求信息,再用AuthenticationEntryPoint去处理;如果是登陆用户,就用AccessDeniedHandler去处理

自定义AuthenticationEntryPoint参考文章:

Spring boot+Security OAuth2 爬坑日记(4)自定义异常处理 上_Yuit-CSDN博客 (opens new window)

Spring Security 自定义 AuthenticationEntryPoint 和 AccessDeniedHandler(匿名/已认证)的用户访问无权限资源时的异常_wangooo的博客-CSDN博客 (opens new window)

# 3.配置简单的权限

这个在身份信息固定,并且不会经常变动的情况下可以按照如下配置,否则不建议这么做,这里只适用于简单的场景.

MyUserDetailsService:

private SocialUserDetails buildUser(String userId) {
		// 根据用户名查找用户信息
		//根据查找到的用户信息判断用户是否被冻结
		/**
		 * 可以从数据库查出来用户名和密码进行比对,为了方便我这里就直接固定了
		 */
		String password = passwordEncoder.encode("123456");
		logger.info("数据库密码是:"+password);
		//注意这里配置角色的时候需要加ROLE_前缀
		return new SocialUser(userId, password,
				true, true, true, true,
				AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN"));
	}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4.权限表达式

之前我们一直都有用到权限表达式,比如最常用的permitAll和Authenticated,这个权限表达式就是允许所有都能访问的意思,其他的相关的权限表达式如下所示:

img (opens new window)

这里可以改写权限的表达如下:

.antMatchers("/user/**").access("hasRole('ADMIN') and hasIpAddress('XXXXXX')")
        .anyRequest()     //所有请求
        .authenticated() //都需身份认证
1
2
3

# 5.spring security控制授权代码封装

image-20210718195026820

AuthorizeConfigProvider:

/**
 * 这个是授权的类
 */
public interface AuthorizeConfigProvider {
    void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config);
}
1
2
3
4
5
6

CityAuthorizeConfigProvider

//配置permitAll的路径
@Component
public class CityAuthorizeConfigProvider implements AuthorizeConfigProvider {
    @Autowired
    private SecurityProperties securityProperties;
    @Override
    public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
        config.antMatchers(
                "/static/**","/page/login","/page/failure","/page/mobilePage",
                "/code/image","/code/sms","/authentication/mobile",securityProperties.getBrower().getSignUPUrl(),
                "/user/register","/page/registerPage","/page/invalidSession","/page/logoutSuccess",securityProperties.getBrower().getSignOutUrl()

        )
        .permitAll();
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

AuthorizeConfigManager: 负责将所有AuthorizeConfigProvider的实现组装起来,并且最后添加一个config.anyRequest().authenticated();

/**
 * AuthorizeConfigManager
 */
public interface AuthorizeConfigManager {
    void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config);
}
1
2
3
4
5
6
@Component
public class CityAuthorizeConfigManager implements AuthorizeConfigManager  {

    @Autowired
    private Set<AuthorizeConfigProvider> authorizeConfigProviders;

    @Override
    public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
        for (AuthorizeConfigProvider authorizeConfigProvider : authorizeConfigProviders) {
            authorizeConfigProvider.config(config);
        }
        //除了自己配置的CityAuthorizeConfigProvider的内容外 其他的都需要认证
        config.anyRequest().authenticated();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
authorizationConfigurerManager().config(http.authorizeRequests()); // 其它的请求需要认证
1

# 6、rbac权限

image-20210718232043114

@Component
@Transactional
public class RbacUserDetailsService implements UserDetailsService {

	private Logger logger = LoggerFactory.getLogger(getClass());
	
	@Autowired
	private AdminRepository adminRepository;

	/**
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.core.userdetails.UserDetailsService#
	 * loadUserByUsername(java.lang.String)
	 */
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		logger.info("表单登录用户名:" + username);
		Admin admin = adminRepository.findByUsername(username);
		admin.getUrls();
		return admin;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Component
@Order(Integer.MAX_VALUE)
public class RbacAuthorizeConfigProvider implements AuthorizeConfigProvider {

    /** (non-Javadoc)
     * @see com.imooc.security.core.authorize.AuthorizeConfigProvider#config(org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry)
     */
    @Override
    public boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
        config
                .antMatchers(HttpMethod.GET, "/fonts/**").permitAll()
                .antMatchers(HttpMethod.GET,
                        "/**/*.html",
                        "/admin/me",
                        "/resource").authenticated()
                // 除了上面的请求,对于其他所有请求都使用如下的表达式判断是否有权限
                .anyRequest()
                .access("@rbacService.hasPermission(request, authentication)");
        // 返回true表示当前配置包含了anyRequest配置
        return true;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 7、注解式授权

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .anyRequest().permitAll();
    }
}
1
2
3
4
5
6
7
8
9
10

在controller或者service上添加注解, 注解式授权是基于aop

主要注解有:

  • @Secured用户具有某个角色,可以访问方法
  • @PreAuthorize适合进入方法前的权限认证,@PreAuthorize可以将登陆用户的roles/permissions参数传入方法中
  • @PostAuthorize在方法执行之后校验,适合验证带有返回值的权限
  • @PostFilter:方法返回数据进行过滤权限验证之后对数据进行过滤 留下用户名是 admin1 的数据,表达式中的 filterObject 引用的是方法返回值 List 中的某一个元素
  • @PreFilter: 传入方法数据进行过滤,进入控制器之前对数据进行过滤

img

参考: SpringSecurity认证授权注解使用_鲁健源的博客-CSDN博客 (opens new window)

参考文章:

Spring Security 控制授权 - 简书 (jianshu.com) (opens new window)

SpringSecurity控制授权(鉴权)功能介绍 - charlyFeng - 博客园 (cnblogs.com) (opens new window)

Mr-zhango/imooc-SpringSecurity: imooc-SpringSecurity (github.com) (opens new window)

jojozhai (github.com) (opens new window)

Springsecurity Session会话管理
权限模型

← Springsecurity Session会话管理 权限模型→

最近更新
01
国际象棋
09-15
02
成语
09-15
03
自然拼读
09-15
更多文章>
Theme by Vdoing | Copyright © 2019-2023 zxpnet | 粤ICP备14079330号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式