文章目录
设置通用父工程依赖
在微服务构建中,我们一般用一个父工程来通知管理依赖的各种版本号信息。父工程pom文件如下:
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.zjq</groupId><artifactId>oauth2-demo</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><modules><module>commons</module><module>ms-gateway</module><module>ms-oauth2-server</module><module>ms-registry</module></modules><!-- 可以集中定义依赖资源的版本信息 --><properties><spring-boot-version>2.3.7.RELEASE</spring-boot-version><spring-cloud-version>Hoxton.SR9</spring-cloud-version><lombok-version>1.18.16</lombok-version><commons-lang-version>3.11</commons-lang-version><mybatis-starter-version>2.1.3</mybatis-starter-version><mysql-version>8.0.22</mysql-version><swagger-starter-version>2.1.5-RELEASE</swagger-starter-version><hutool-version>5.4.7</hutool-version><guava-version>20.0</guava-version><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><!-- 集中定义依赖,不引入 --><dependencyManagement><dependencies><!-- spring boot 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot-version}</version><type>pom</type><scope>import</scope></dependency><!-- spring cloud 依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud-version}</version><type>pom</type><scope>import</scope></dependency><!-- lombok 依赖 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok-version}</version></dependency><!-- common-lang3 依赖 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>${commons-lang-version}</version></dependency><!-- mybatis 依赖 --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis-starter-version}</version></dependency><!-- swagger 依赖 --><dependency><groupId>com.battcn</groupId><artifactId>swagger-spring-boot-starter</artifactId><version>${swagger-starter-version}</version></dependency><!-- mysql 依赖 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql-version}</version></dependency><!-- hutool 依赖 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>${hutool-version}</version></dependency><!-- guava 依赖 --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>${guava-version}</version></dependency></dependencies></dependencyManagement><!-- 集中定义项目所需插件 --><build><pluginManagement><plugins><!-- spring boot maven 项目打包插件 --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></pluginManagement></build></project>
构建eureka注册中心
在SpringCloud微服务体系中服务注册中心是一个必要的存在,通过注册中心提供服务的注册和发现。具体细节可以查看我之前的博客,这里不再赘述。我们开始构建一个eureka注册中心,对应的yml配置文件如下:
server:port:8080spring:application:# 应用名称name: ms-registry
# 配置 Eureka Server 注册中心eureka:client:register-with-eureka:falsefetch-registry:falseservice-url:defaultZone: http://localhost:8080/eureka/
logging:pattern:console:'%d{HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n'
对应的项目启动类代码如下:
packagecom.zjq.msregistry;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.netflix.eureka.server.EnableEurekaServer;/**
* 注册中心
* @author zjq
*///启动 eureka注册中心服务端相关组件@EnableEurekaServer@SpringBootApplicationpublicclassMsRegistryApplication{publicstaticvoidmain(String[] args){SpringApplication.run(MsRegistryApplication.class, args);}}
至此,一个单体的服务注册中心搭建完成。
构建认证授权服务
上文我们已经完成了注册中心的搭建,接下来我们开始搭建认证授权中心。
配置文件设置
我们同样在父工程下面新建一个子工程,作为认证授权中心的微服务。对应的yml文件和pom文件配置如下:
application.yml
server:port:8082# 端口spring:application:name: ms-oauth2-server # 应用名# 数据库datasource:driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password:123456url: jdbc:mysql://127.0.0.1:3306/oauth2?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false# Redisredis:port:6379host: localhost
timeout:3000database:1password:123456# swaggerswagger:base-package: com.zjq.oauth2
title: 认证服务API接口文档
# Oauth2client:oauth2:client-id: appId # 客户端标识 IDsecret:123456# 客户端安全码# 授权类型grant_types:- password
- refresh_token
# token 有效时间,单位秒token-validity-time:3600refresh-token-validity-time:3600# 客户端访问范围scopes:- api
- all
# 配置 Eureka Server 注册中心eureka:instance:prefer-ip-address:trueinstance-id: ${spring.cloud.client.ip-address}:${server.port}client:service-url:defaultZone: http://localhost:8080/eureka/
# Mybatismybatis:configuration:map-underscore-to-camel-case:true# 开启驼峰映射# 指标监控健康检查management:endpoints:web:exposure:include:"*"# 暴露的端点logging:pattern:console:'%d{HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n'
pom.xml
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>oauth2-demo</artifactId><groupId>com.zjq</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>ms-oauth2-server</artifactId><dependencies><!-- eureka client --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><!-- spring web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- spring data redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- mybatis --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!-- mysql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- spring cloud security --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId></dependency><!-- spring cloud oauth2 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><!-- commons 公共项目 --><dependency><groupId>com.zjq</groupId><artifactId>commons</artifactId><version>1.0-SNAPSHOT</version></dependency><!-- 自定义的元数据依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency></dependencies></project>
Security配置类
我们开始搭建Spring Security相关的配置类,具体配置类代码如下:
packagecom.zjq.oauth2.server.config;importcn.hutool.crypto.digest.DigestUtil;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.data.redis.connection.RedisConnectionFactory;importorg.springframework.security.authentication.AuthenticationManager;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;importjavax.annotation.Resource;/**
* Security 配置类
* @author zjq
*/@Configuration@EnableWebSecuritypublicclassSecurityConfigurationextendsWebSecurityConfigurerAdapter{// 注入 Redis 连接工厂@ResourceprivateRedisConnectionFactory redisConnectionFactory;/**
* 初始化 RedisTokenStore 用于将 token 存储至 Redis
* @return
*/@BeanpublicRedisTokenStoreredisTokenStore(){RedisTokenStore redisTokenStore =newRedisTokenStore(redisConnectionFactory);
redisTokenStore.setPrefix("TOKEN:");// 设置key的层级前缀,方便查询return redisTokenStore;}// 初始化密码编码器,用 MD5 加密密码@BeanpublicPasswordEncoderpasswordEncoder(){returnnewPasswordEncoder(){/**
* 加密
* @param rawPassword 原始密码
* @return
*/@OverridepublicStringencode(CharSequence rawPassword){returnDigestUtil.md5Hex(rawPassword.toString());}/**
* 校验密码
* @param rawPassword 原始密码
* @param encodedPassword 加密密码
* @return
*/@Overridepublicbooleanmatches(CharSequence rawPassword,String encodedPassword){returnDigestUtil.md5Hex(rawPassword.toString()).equals(encodedPassword);}};}// 初始化认证管理对象@Bean@OverridepublicAuthenticationManagerauthenticationManagerBean()throwsException{returnsuper.authenticationManagerBean();}// 放行和认证规则@Overrideprotectedvoidconfigure(HttpSecurity http)throwsException{
http.csrf().disable().authorizeRequests()// 放行的请求.antMatchers("/oauth/**","/actuator/**").permitAll().and().authorizeRequests()// 其他请求必须认证才能访问.anyRequest().authenticated();}}
Security配置类主要完成以下配置:
- 注入 Redis 连接工厂
- 初始化 RedisTokenStore 用于将 token 存储至 Redis
- 初始化密码编码器,用 MD5 加密密码
- 初始化认证管理对象
- 设置放行和认证规则
授权服务配置类
配置完了security配置类后,我们开始编写授权服务配置类,授权服务配置类需要继承
AuthorizationServerConfigurerAdapter
并重写对应的方法,tips:idea子类重写父类快捷键是
Ctrl+O
,重写后的授权服务配置类如下:
packagecom.zjq.oauth2.server.config;importcom.zjq.commons.model.domain.SignInIdentity;importcom.zjq.oauth2.server.service.UserService;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.authentication.AuthenticationManager;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.oauth2.common.DefaultOAuth2AccessToken;importorg.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;importorg.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;importorg.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;importorg.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;importorg.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;importorg.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;importjavax.annotation.Resource;importjava.util.LinkedHashMap;/**
* 授权服务配置类
* @author zjq
*/@Configuration@EnableAuthorizationServerpublicclassAuthorizationServerConfigurationextendsAuthorizationServerConfigurerAdapter{// RedisTokenSore@ResourceprivateRedisTokenStore redisTokenStore;// 认证管理对象@ResourceprivateAuthenticationManager authenticationManager;// 密码编码器@ResourceprivatePasswordEncoder passwordEncoder;// 客户端配置类@ResourceprivateClientOAuth2DataConfiguration clientOAuth2DataConfiguration;// 登录校验@ResourceprivateUserService userService;/**
* 配置令牌端点安全约束
*
* @param security
* @throws Exception
*/@Overridepublicvoidconfigure(AuthorizationServerSecurityConfigurer security)throwsException{// 允许访问 token 的公钥,默认 /oauth/token_key 是受保护的
security.tokenKeyAccess("permitAll()")// 允许检查 token 的状态,默认 /oauth/check_token 是受保护的.checkTokenAccess("permitAll()");}/**
* 客户端配置 - 授权模型
*
* @param clients
* @throws Exception
*/@Overridepublicvoidconfigure(ClientDetailsServiceConfigurer clients)throwsException{
clients.inMemory().withClient(clientOAuth2DataConfiguration.getClientId())// 客户端标识 ID.secret(passwordEncoder.encode(clientOAuth2DataConfiguration.getSecret()))// 客户端安全码.authorizedGrantTypes(clientOAuth2DataConfiguration.getGrantTypes())// 授权类型.accessTokenValiditySeconds(clientOAuth2DataConfiguration.getTokenValidityTime())// token 有效期.refreshTokenValiditySeconds(clientOAuth2DataConfiguration.getRefreshTokenValidityTime())// 刷新 token 的有效期.scopes(clientOAuth2DataConfiguration.getScopes());// 客户端访问范围}/**
* 配置授权以及令牌的访问端点和令牌服务
*
* @param endpoints
* @throws Exception
*/@Overridepublicvoidconfigure(AuthorizationServerEndpointsConfigurer endpoints)throwsException{// 认证器
endpoints.authenticationManager(authenticationManager)// 具体登录的方法.userDetailsService(userService)// token 存储的方式:Redis.tokenStore(redisTokenStore);}}
上面用到的客户端配置类如下:
packagecom.zjq.oauth2.server.config;importlombok.Data;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.stereotype.Component;/**
* 客户端配置类
* @author zjq
*/@Component@ConfigurationProperties(prefix ="client.oauth2")@DatapublicclassClientOAuth2DataConfiguration{// 客户端标识 IDprivateString clientId;// 客户端安全码privateString secret;// 授权类型privateString[] grantTypes;// token有效期privateint tokenValidityTime;/**
* refresh-token有效期
*/privateint refreshTokenValidityTime;/**
* 客户端访问范围
*/privateString[] scopes;}
具体登录的方法实现:
登录实现
packagecom.zjq.oauth2.server.service;importcom.zjq.commons.model.domain.SignInIdentity;importcom.zjq.commons.model.pojo.Users;importcom.zjq.commons.utils.AssertUtil;importcom.zjq.oauth2.server.mapper.UsersMapper;importorg.springframework.beans.BeanUtils;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;/**
* 登录校验
* @author zjq
*/@ServicepublicclassUserServiceimplementsUserDetailsService{@ResourceprivateUsersMapper usersMapper;@OverridepublicUserDetailsloadUserByUsername(String username)throwsUsernameNotFoundException{AssertUtil.isNotEmpty(username,"请输入用户名");Users users = usersMapper.selectByAccountInfo(username);if(users ==null){thrownewUsernameNotFoundException("用户名或密码错误,请重新输入");}// 初始化登录认证对象SignInIdentity signInIdentity =newSignInIdentity();// 拷贝属性BeanUtils.copyProperties(users, signInIdentity);return signInIdentity;}}
UsersMapper:
packagecom.zjq.oauth2.server.mapper;importcom.zjq.commons.model.pojo.Users;importorg.apache.ibatis.annotations.Param;importorg.apache.ibatis.annotations.Select;/**
* 用户 Mapper
* @author zjq
*/publicinterfaceUsersMapper{/**
*
* 根据用户名 or 手机号 or 邮箱查询用户信息
*
* @param account
* @return
*/@Select("select id, username, nickname, phone, email, "+"password, avatar_url, roles, is_valid from t_users where "+"(username = #{account} or phone = #{account} or email = #{account})")UsersselectByAccountInfo(@Param("account")String account);}
用户实体:
packagecom.zjq.commons.model.pojo;importcom.zjq.commons.model.base.BaseModel;importlombok.Getter;importlombok.Setter;/**
* 用户实体类
*
* @Author zjq
* @Date 2022/10/12
*/@Getter@SetterpublicclassUsersextendsBaseModel{// 主键privateInteger id;// 用户名privateString username;// 昵称privateString nickname;// 密码privateString password;// 手机号privateString phone;// 邮箱privateString email;// 头像privateString avatarUrl;// 角色privateString roles;}
packagecom.zjq.commons.model.base;importlombok.Getter;importlombok.Setter;importjava.io.Serializable;importjava.util.Date;/**
* 实体对象公共属性
*
* @Author zjq
* @Date 2022/10/12
*/@Getter@SetterpublicclassBaseModelimplementsSerializable{privateInteger id;privateDate createDate;privateDate updateDate;privateint isValid;}
到此,我们完成了认证授权服务构建,接下来我们进行测试验证:
测试验证
我们启动注册中心和认证授权微服务。访问注册中心:http://localhost:8080/
可以看到认证授权服务已经注册到注册中心。
接下来我们通过postman访问请求token测试:
Authorization请求头中配置,username和password,对应oauth客户端中的配置:
在body中配置请求参数,发起请求后返回如下:
在Redis中我们也可以看到生成的相关token配置:
至此,我们完成了认证授权中心的初步搭建。
本文内容到此结束了,
如有收获欢迎点赞👍收藏💖关注✔️,您的鼓励是我最大的动力。
如有错误❌疑问💬欢迎各位指出。
主页:共饮一杯无的博客汇总👨💻保持热爱,奔赴下一场山海。🏃🏃🏃
版权归原作者 共饮一杯无 所有, 如有侵权,请联系我们删除。