赞
踩
代码仓库:地址
代码分支:lesson6
博客:地址
在先前文章中,我们使用SpringSecurity OAuth2搭建了一套基于OAuth2协议的授权系统,并扩展了手机验证码授权模式。在微服务架构下,网关承担着流量入口的角色,所有的请求都要先经过网关,然后由网关负责转发到具体的服务,因此可以在网关实现统一鉴权,网关对请求中的权限进行鉴定,然后将权限信息转发到具体的资源服务,在资源服务中只需要简单校验请求中的权限信息即可(查看信息是否有效),整体流程如下所示:
我们在上一篇的基础上引入网关服务,在这里使用SpringCloud Gateway组件进行搭建,引入依赖:
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-gateway</artifactId>
- </dependency>
网关在OAuth2授权协议中承担着资源服务的角色,对请求进行身份鉴定和访问权限控制,身份鉴定需要访问OAuth2授权服务,因此需要引入OAuth2资源服务以及客户端依赖:
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-oauth2-resource-server</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-oauth2-client</artifactId>
- </dependency>
通过之前的文章,我们可以知道SpringSecurity 通过组装一系列的Filter来完成身份验证和权限访问控制功能,但是SpringCloud Gateway使用了新技术框架Reactive Stack(响应式编程),在Spring中提供了Spring WebFlux模块支持响应式编程,传统的Spring MVC都是基于阻塞I/O编程,而Spring WebFlux是基于非阻塞I/O,我们不再这里讨论这两个的区别,只需要知道WebFlux特别适合I/O密集型性应用,网关就是典型的I/O密集应用(网络I/O处理频繁)。SpringSecurity对WebFlux提供了支持,在WebFlux中WebFilter组件承担着与Filter相似的功能。
我们在先前的应用中通过HttpSecurity组件来组装SpringSecurity功能,在这里要使用新的组件ServerHttpSecurity来组装SpringSecurity功能,配置如下所示:
- ///启用WebFlux下的SpringSecurity配置
- @EnableWebFluxSecurity
- public class ResourceServerConfig {
- 访问权限验证
- @Autowired
- AuthManagerHandler authManagerHandler;
- 无权限访问处理器
- @Autowired
- AccessDeniedHandler accessDeniedHandler;
- /// 登录信息失效处理器
- @Autowired
- LoginLoseHandler loginLoseHandler;
- 访问白名单,对白名单路径可以实现匿名访问
- @Autowired
- private WhiteUrlProperties whiteUrlProperties;
-
- @Bean
- public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
- http.oauth2ResourceServer()
- /// 这里配置对令牌的校验,从OAuth2授权服务中获取令牌对应的授权信息
- .opaqueToken()
- ///令牌校验地址,用于校验令牌是否有效,已经令牌对应的授权信息
- .introspectionUri("http://localhost:8081/oauth/check_token")
- 客户端信息
- .introspectionClientCredentials("blog", "blog")
- .and()
- .accessDeniedHandler(accessDeniedHandler)
- .authenticationEntryPoint(loginLoseHandler)
- .and().authorizeExchange()
- .pathMatchers(HttpMethod.OPTIONS).permitAll() //o
- .pathMatchers("/**").access(authManagerHandler)
- .anyExchange().authenticated()
- .and()
- .addFilterBefore(securityGlobalFilter(whiteUrlProperties), SecurityWebFiltersOrder.FIRST)
- .cors().disable().csrf().disable();
- return http.build();
- }
- /// 该过滤器实现将获取到的授权信息转发到下游服务中,方便后续校验
- public WebFilter securityGlobalFilter(WhiteUrlProperties properties) {
- return new SecurityGlobalFilter(properties);
- }
-
- }
网关路由配置以及其它细节信息可以前往代码仓库进行查看,在此不做过多解释。
资源服务器也需要做一些调整,不需要对请求进行严格的访问控制,只需要校验网关传递的授权信息即可,然后将授权信息放入到SecurityContext中方便后续处理,同时需要注意在资源服务中还是使用Spring MVC框架进行处理(Spring WebFlux可以提高系统吞吐量,但是也会增加编程难度,例如原先的线程变量将不适用,具体需要考量整体编程人员掌握的技术栈来做决定)。
这里的资源服务器不再依赖OAuth授权服务,因此可以移除@EnableResourceServer配置(不直接参与权限控制,只需要校验上游传递的授权信息是否有效即可),同时增加对上游SpringCloud Gateway传递的授权信息进行解析处理,增加自定义SecurityAuthTokenFilter组件:
- public class SecurityAuthTokenFilter extends OncePerRequestFilter {
- private static final String AUTH_TOKEN_NAME = "token";
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
- FilterChain filterChain) throws ServletException, IOException {
- String token = request.getHeader(AUTH_TOKEN_NAME);
- if (StringUtils.isEmpty(token)) {
- /// 继续处理
- filterChain.doFilter(request, response);
- return;
- }
- ///....省略处理细节,具体前往代码仓库进行查看
- 创建自定义的Authentication对象,必须申明为已授权,也就是isAuthenticated()方法返回为true
- BlogAuthentication authentication = new BlogAuthentication(userId, clientId, authorities);
- //....省略处理细节,具体前往代码仓库进行查看
- /// 将授权信息放入到SecurityContext中,方便后续使用
- SecurityContextHolder.getContext().setAuthentication(authentication);
- filterChain.doFilter(request, response);
- }
- }
分别运行Gateway网关服务、OAuth授权服务、Resource资源服务
使用密码模式进行授权登录,发送请求POST http://localhost:8080/blog-oauth/oauth/token,请求参数:
- client_id:blog
- client_secret:blog
- grant_type:password
- username:admin
- password:admin
返回结果:
- {
- "access_token": "4aace702-cc9d-4a92-b507-9b65f192a65f",
- "token_type": "bearer",
- "refresh_token": "a50a6cff-97b0-4d0f-b3d2-e0fdcee6f142",
- "expires_in": 5591,
- "scope": "all user"
- }
使用得到的access_token访问资源服务器中的/admin/hello接口,发送请求GET http://localhost:8080/blog-resource/admin/hello,请求头中携带参数:
Authorization:Bearer 4aace702-cc9d-4a92-b507-9b65f192a65f
返回结果:
- {
- "code": 200,
- "data": "Hello Admin"
- }
访问其他权限的接口,发送请求GET http://localhost:8080/blog-resource/user/hello,请求头中携带参数:
Authorization:Bearer 4aace702-cc9d-4a92-b507-9b65f192a65f
返回结果:
- {
- "code": 400,
- "message": "无权限访问"
- }
至此得到期望的访问结果,实现了统一权限控制
技术更新换代速度很快,我们无法在有限时间掌握全部知识,但我们可以在他人的基础上进行快速学习,学习也是枯燥无味的,加入我们学习牛人经验:
点击:加群讨论
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。