微服务集成Spring Security + Oauth2 + JWT+Swagger2 + Druid

2024-02-11 17:08

本文主要是介绍微服务集成Spring Security + Oauth2 + JWT+Swagger2 + Druid,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考《深入理解Spring Cloud与微服务构建》 感谢作者 方志朋
参考 https://blog.csdn.net/yuanlaijike/category_9283872.html
参考 https://www.jianshu.com/p/19059060036b

文章目录

      • 背景介绍
      • 实现目标
      • 实现过程
          • 集成网关zuul
          • 集成oauth2
          • 集成swagger2
          • 集成Druid
      • 验证功能

背景介绍

本文是下文的进阶篇,进一步以微服务为基础进行集成Spring Security + Oauth2 + JWT+Swagger2 + Druid
微服务自动化部署SpringCloud+Dockerfile+docker-compose+git+Maven

实现目标

把网关服务和验证服务集成到一个微服务中,验证服务以oauth2+jwt进行实现,同时使用swagger2简单明了的展示验证登入的相关接口,druid方便管理数据库连接池以及性能排查

  • 模块authservice
    • 集成网关zuul 取消原ui模块网关
    • 集成Spring Security Oauth2验证+鉴权
      • Oauth server 获取令牌token
      • Oauth client 用户注册API
  • GibHub项目代码 分支master

实现过程

集成网关zuul
//注意 Spring Boot 1.2x的zuul与oauth2集成会报spring注入错误和unable to start embedded tomcat错误
//Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.cloud.security.oauth2.resource.ResourceServerProperties org.springframework.cloud.security.oauth2.proxy.OAuth2ProxyAutoConfiguration.resourceServerProperties; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.cloud.security.oauth2.resource.ResourceServerProperties] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
//参考:https://github.com/spring-cloud/spring-cloud-security/issues/73
//解决办法是升级 Spring Cloud 版本为Brixton.SR5,Spring Boot版本为1.3.5
//新增模块 authservice,网关zuul可参考ui模块配置
//包含 config、eureka、feign、ribbon、hystrix、zuul//config模块添加文件 authservice.yml
ribbon:ReadTimeout: 60000ConnectTimeout: 20000zuul:host:connect-timeout-millis: 20000socket-timeout-millis: 60000routes:authservice:path: /uiservice/**# 相当于把http://localhost/uiservice/xxx中的/xxx段映射到原来的http://localhost/ui/xxx serviceId: uisensitiveHeaders:
集成oauth2
//添加依赖oauth2,默认包含security、oauth2、jwt、lombok包
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>//oauth2是实现Spring Security验证模块,所以先直接进行Spring Security的配置
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class GlobalMethodSecurityConfiguration {}@Configuration
class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overrideprotected void configure(HttpSecurity http) throws Exception {//CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。http.csrf().disable().exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {@Overridepublic void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);}}).and().authorizeRequests().antMatchers("/**").authenticated().and().httpBasic();}@AutowiredUserServiceDetail userServiceDetail;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userServiceDetail).passwordEncoder(new BCryptPasswordEncoder());}
}//注意上面注入的userServiceDetail需要自己实现接口UserDetailsService,security jpa实体表可以自动创建,但是oauth的表需要自行创建,语句见源码的sql.sql
@Service
public class UserServiceDetail implements UserDetailsService {@Autowiredprivate UserDao userRepository;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return userRepository.findByUsername(username);}
}//同时也是资源服务器,配置如下
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter{Logger log = LoggerFactory.getLogger(ResourceServerConfiguration.class);@Overridepublic void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().regexMatchers(".*swagger.*",".*v2.*",".*webjars.*","/user/login.*","/user/registry.*","/user/test.*",".*druid.*").permitAll().antMatchers("/**").authenticated();}@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {log.info("Configuring ResourceServerSecurityConfigurer ");resources.resourceId("authservice").tokenStore(tokenStore);}@AutowiredTokenStore tokenStore;
}//oauth2配置,定义内部oauth server的basic验证用户名密码,并加入jwt私钥解密方式
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory().withClient("authservice").secret("123456").scopes("service").autoApprove(true).authorizedGrantTypes("implicit","refresh_token", "password", "authorization_code").accessTokenValiditySeconds(24*3600);//24小时过期}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.tokenStore(tokenStore()).tokenEnhancer(jwtTokenEnhancer()).authenticationManager(authenticationManager);}@Autowired@Qualifier("authenticationManagerBean")private AuthenticationManager authenticationManager;@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(jwtTokenEnhancer());}@Beanprotected JwtAccessTokenConverter jwtTokenEnhancer() {KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("fzp-jwt.jks"), "fzp123".toCharArray());JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter.setKeyPair(keyStoreKeyFactory.getKeyPair("fzp-jwt"));return converter;}
}//jwt配置,公钥加密方式
@Configuration
public class JwtConfiguration {@AutowiredJwtAccessTokenConverter jwtAccessTokenConverter;@Bean@Qualifier("tokenStore")public TokenStore tokenStore() {System.out.println("Created JwtTokenStore");return new JwtTokenStore(jwtAccessTokenConverter);}@Beanprotected JwtAccessTokenConverter jwtTokenEnhancer() {JwtAccessTokenConverter converter =  new JwtAccessTokenConverter();Resource resource = new ClassPathResource("public.cert");String publicKey ;try {publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));} catch (IOException e) {throw new RuntimeException(e);}converter.setVerifierKey(publicKey);return converter;}
}
集成swagger2
//加入依赖<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.7.0</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.7.0</version></dependency>//配置swagger2
@Configuration
@EnableSwagger2
public class SwaggerConfig {/*** 全局参数** @return*/private List<Parameter> parameter() {List<Parameter> params = new ArrayList<>();params.add(new ParameterBuilder().name("Authorization").description("Authorization Bearer token").modelRef(new ModelRef("string")).parameterType("header").required(false).build());return params;}@Beanpublic Docket sysApi() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.basePackage("com.xiaofeng.authservice.controller")).paths(PathSelectors.any()).build().globalOperationParameters(parameter());}private ApiInfo apiInfo() {return new ApiInfoBuilder().title(" authservice api ").description("authservice 微服务").termsOfServiceUrl("").contact("xiaofeng").version("1.0").build();}
}//添加controller
@RestController
@RequestMapping("/user")
public class UserController {@AutowiredUserService userService;@ApiOperation(value = "注册", notes = "username和password为必选项")@RequestMapping(value = "/registry",method = RequestMethod.POST)public User createUser(@RequestBody User user){//参数判读省略,判读该用户在数据库是否已经存在省略String entryPassword= BPwdEncoderUtils.BCryptPassword(user.getPassword());user.setPassword(entryPassword);return userService.createUser(user);}@ApiOperation(value = "登录", notes = "username和password为必选项")@RequestMapping(value = "/login",method = RequestMethod.POST)public RespDTO login(@RequestParam String username , @RequestParam String password){//参数判读省略return   userService.login(username,password);}@ApiOperation(value = "根据用户名获取用户", notes = "根据用户名获取用户")@RequestMapping(value = "/{username}",method = RequestMethod.POST)@PreAuthorize("hasRole('USER')")// @PreAuthorize("hasAnyAuthority('ROLE_USER')")public RespDTO getUserInfo(@PathVariable("username") String username){//参数判读省略User user=  userService.getUserInfo(username);return RespDTO.onSuc(user);}
}
集成Druid
//数据库连接池druid
//config模块的文件 authservice.yml添加
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf-8username: rootpassword: rootinitialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: truemaxPoolPreparedStatementPerConnectionSize: 20filters: stat,wall,log4jconnectionProperties:druid.stat.mergeSql: truedruid.stat.slowSqlMillis: 5000useGlobalDataSourceStat: truejpa:hibernate:ddl-auto: updateshow-sql: true//Druid配置
@Configuration
public class DruidConfiguration {@Beanpublic ServletRegistrationBean DruidStatViewServle2() {ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");//白名单:servletRegistrationBean.addInitParameter("allow", "127.0.0.1");//IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.//servletRegistrationBean.addInitParameter("deny", "192.168.1.73");//登录查看信息的账号密码.servletRegistrationBean.addInitParameter("loginUsername", "admin2");servletRegistrationBean.addInitParameter("loginPassword", "123456");//是否能够重置数据.servletRegistrationBean.addInitParameter("resetEnable", "false");return servletRegistrationBean;}/*** 注册一个:filterRegistrationBean** @return*/@Beanpublic FilterRegistrationBean druidStatFilter2() {FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());//添加过滤规则.filterRegistrationBean.addUrlPatterns("/*");//添加不需要忽略的格式信息.filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");return filterRegistrationBean;}}

验证功能

  • Spring Security + Oauth2 + JWT+Swagger2
    按顺序启动 Discovery、Config、Authservice
    首先打开swagger2,查看现有接口:http://localhost/swagger-ui.html
    使用curl进行请求验证,参考:
    https://blog.csdn.net/suicuney/article/details/103847400
//模拟登入
curl -X POST -d username=admin -d password=admin http://localhost:9999/user/login
//模拟从oauth server 获取token
curl -X POST -u authservice:123456 http://localhost:9999/oauth/token -d grant_type=password -d username=admin -d password=admin
//模拟从oauth server 获取token 另一种写法
curl -X POST -H "Authorization: Basic YXV0aHNlcnZpY2U6MTIzNDU2" -d grant_type=password -d username=admin -d password=admin http://localhost:9999/oauth/token
//模拟用登入得到token进行访问有权限的controll接口
curl -X POST -H "Authorization:BearereyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE1Nzg1Mzc4NDIsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiVVNFUiJdLCJqdGkiOiI4Y2JkYjRjMy0wOTMxLTQyMWItYWYwOS1lMDViYzYyODFhY2YiLCJjbGllbnRfaWQiOiJhdXRoc2VydmljZSIsInNjb3BlIjpbInNlcnZpY2UiXX0.JNdRVRcOau0uTmUdSVdImeQPcIf6PcBzIL3bdfm0856ou9EjEiGDbqike1nw2DueR3Kq5AnbtsYuiPA_sEuwamFLy3H2Eezo7y-5DX26fId7PufHWn1aSshsW5zQNGORr47xZ8_oXq2J5yfwzCrDNDzqbgkcOAB7jWTD9DcOPUig2FCvA0AglZxt442W34N_Sds6l8C6Hy9Dl2hlzAoe0VCy_yCv2APnwNhX4KWnFJTZsEK9LeYgwvlM0nPz6JOYwOlLSk4P8geC0zuspoJ0Ve9mXU4qHzX040amrSjnJooLL1jmsxDVffop6rprkQmuKSkEDfipVfRLx5TUB9xv4g","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbInNlcnZpY2UiXSwiYXRpIjoiOGNiZGI0YzMtMDkzMS00MjFiLWFmMDktZTA1YmM2MjgxYWNmIiwiZXhwIjoxNTgxMDQzNDQyLCJhdXRob3JpdGllcyI6WyJVU0VSIl0sImp0aSI6IjFjNzc3ZjE4LWU0YjEtNGYzNi05ZTE5LTIwYzZiNDRmZGMyOCIsImNsaWVudF9pZCI6ImF1dGhzZXJ2aWNlIn0.YmcMlCzEmLtN_W6XrlP3O5EkH_jbH6Gpa8rxiuVRpW6k9R3k77ZZauE07f1v_dyUL3DGRzuMMGDGfKHOUapJ9gus2UX9-QDe9x9V46hEVkfcHplYwwdC43o8Z6URM4rlA5vJkKbQa6EI1KJVZfLNkfTSXjE2TD3M2MuwJu4xgkNg6Eg25vDxyiFPIsyOBIl66ROJJogS90M7tMrOiCTK40jWTPwrDfOdy7EzJvi0mlCwZmGK9qP2pwB8yi5zcgHT2P0XrPFT_VPDQJS5X7DLU_k-k_mfoHyobtVHoDF7VkUdLngtoqy_ynF-hcJqvHH8PvnwjaVzi448dGpXXBSaxA" http://localhost:9999/user/admin
  • Druid监控
    打开druid监控网址:http://localhost/druid/login.html
    用户名密码为配置类中设置的admin2/123456
    登入后可以查看数据库各种相关信息
    在这里插入图片描述

这篇关于微服务集成Spring Security + Oauth2 + JWT+Swagger2 + Druid的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/700356

相关文章

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操