spring Cloud微服务 security+oauth2认证授权中心自定义令牌增强,并实现登录和退出
文章目录
- 认证授权中心自定义令牌增强
- 自定义认证端点返回结果
- 登录逻辑调整,增强令牌返回参数
- 测试验证
- 用户微服务构建
- 配置类构建
- 相关实体类
- 登录
- 退出登录
在之前的博客我写了 SpringCloud整合spring security+ oauth2+Redis实现认证授权,本文对返回的token实现自定义增强令牌返回结果,以及对于oauth2存在Redis的数据进行解释。
认证授权中心自定义令牌增强
自定义认证端点返回结果
访问oauth/token,oauth2默认返回的授权token信息如下:
如果不自定义可以看到访问oauth/token,默认访问的是TokenEndpoint下的接口
在授权服务中自定义oauth2控制器实现自定义令牌参数返回,代码如下:
package com.zjq.oauth2.server.controller;import com.zjq.commons.model.domain.ResultInfo;
import com.zjq.commons.utils.ResultInfoUtil;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.LinkedHashMap;
import java.util.Map;/*** Oauth2 控制器*/
@RestController
@RequestMapping("oauth")
public class OAuthController {@Resourceprivate TokenEndpoint tokenEndpoint;@Resourceprivate HttpServletRequest request;@PostMapping("token")public ResultInfo postAccessToken(Principal principal, @RequestParam Map<String, String> parameters)throws HttpRequestMethodNotSupportedException {return custom(tokenEndpoint.postAccessToken(principal, parameters).getBody());}/*** 自定义 Token 返回对象** @param accessToken* @return*/private ResultInfo custom(OAuth2AccessToken accessToken) {DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;Map<String, Object> data = new LinkedHashMap(token.getAdditionalInformation());data.put("accessToken", token.getValue());data.put("expireIn", token.getExpiresIn());data.put("scopes", token.getScope());if (token.getRefreshToken() != null) {data.put("refreshToken", token.getRefreshToken().getValue());}return ResultInfoUtil.buildSuccess(request.getServletPath(), data);}}
登录逻辑调整,增强令牌返回参数
添加登录认证对象:
package com.zjq.commons.model.domain;import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;/*** 登录认证对象** @Author zjq* @Date 2022/10/12*/
@Getter
@Setter
public class SignInIdentity implements UserDetails {// 主键private Integer id;// 用户名private String username;// 昵称private String nickname;// 密码private String password;// 手机号private String phone;// 邮箱private String email;// 头像private String avatarUrl;// 角色private String roles;// 是否有效 0=无效 1=有效private int isValid;// 角色集合, 不能为空private List<GrantedAuthority> authorities;// 获取角色信息@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if (StrUtil.isNotBlank(this.roles)) {// 获取数据库中的角色信息Lists.newArrayList();this.authorities = Stream.of(this.roles.split(",")).map(role -> {return new SimpleGrantedAuthority(role);}).collect(Collectors.toList());} else {// 如果角色为空则设置为 ROLE_USERthis.authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");}return this.authorities;}@Overridepublic String getPassword() {return this.password;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return this.isValid == 0 ? false : true;}}
登录后返回登录认证对象:
@Resourceprivate UsersMapper usersMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {AssertUtil.isNotEmpty(username, "请输入用户名");Users users = usersMapper.selectByAccountInfo(username);if (users == null) {throw new UsernameNotFoundException("用户名或密码错误,请重新输入");}// 初始化登录认证对象SignInIdentity signInIdentity = new SignInIdentity();// 拷贝属性BeanUtils.copyProperties(users, signInIdentity);return signInIdentity;}
在授权服务配置类AuthorizationServerConfiguration中增强令牌返回信息:
/*** 配置授权以及令牌的访问端点和令牌服务** @param endpoints* @throws Exception*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {// 认证器endpoints.authenticationManager(authenticationManager)// 具体登录的方法.userDetailsService(userService)// token 存储的方式:Redis.tokenStore(redisTokenStore)// 令牌增强对象,增强返回的结果.tokenEnhancer((accessToken, authentication) -> {// 获取登录用户的信息,然后设置SignInIdentity signInIdentity = (SignInIdentity) authentication.getPrincipal();LinkedHashMap<String, Object> map = new LinkedHashMap<>();map.put("nickname", signInIdentity.getNickname());map.put("avatarUrl", signInIdentity.getAvatarUrl());DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;token.setAdditionalInformation(map);return token;});}
测试验证
访问请求/oauth/token,可以看到已经返回我们自己需要的认证授权返回结果。
至此,认证授权微服务已经构建完成。
上述已经完成了认证授权中心的搭建。下面继续通过用户微服务访问认证中心实现登录退出。
接下来我们构建一个用户微服务并通过调用授权认证服务实现登录和退出。
用户微服务构建
用户服务相关pom依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="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-users</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><!-- 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>
配置文件内容如下:
server:# 端口port: 8082 spring:application:# 应用名name: ms-users # 数据库datasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456url: jdbc:mysql://127.0.0.1:3306/oauth2?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false# Redisredis:port: 6379host: localhosttimeout: 3000database: 1password: 123456# swaggerswagger:base-package: com.zjq.oauth2title: 用户服务API接口文档# Oauth2 客户端信息
oauth2:client:client-id: appIdsecret: 123456grant_type: passwordscope: api# 配置 Eureka Server 注册中心
eureka:instance:prefer-ip-address: trueinstance-id: ${spring.cloud.client.ip-address}:${server.port}client:service-url:defaultZone: http://localhost:8080/eureka/# oauth2 服务地址
service:name:ms-oauth-server: http://ms-oauth2-server/# Mybatis
mybatis: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'
配置类构建
oauth2客户端配置类构建:
/*** oauth2 客户端配置类* @author zjq*/
@Component
@ConfigurationProperties(prefix = "oauth2.client")
@Getter
@Setter
public class OAuth2ClientConfiguration {private String clientId;private String secret;private String grant_type;private String scope;}
编写redisTemplate相关配置类,调整默认的序列化方式。
编写远程请求配置类:
/*** Rest 配置类*/
@Configuration
public class RestTemplateConfiguration {// 负载均衡请求@LoadBalanced@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}}
相关实体类
申请授权返回实体:
/*** 申请授权返回实体* @author zjq*/
@Getter
@Setter
public class OAuthUserInfo implements Serializable {private String nickname;private String avatarUrl;private String accessToken;private String expireIn;private List<String> scopes;private String refreshToken;}
登录成功返回实体:
/*** 登录成功返回实体* @author zjq*/
@Setter
@Getter
public class LoginUserInfo implements Serializable {private String nickname;private String token;private String avatarUrl;}
登录
登录功能相关代码如下:
/*** 登录** @param account* @param password* @return*/@GetMapping("signin")public ResultInfo signIn(String account, String password) {return userService.signIn(account, password, request.getServletPath());}
/*** 登录** @param account 帐号:用户名或手机或邮箱* @param password 密码* @param path 请求路径* @return*/public ResultInfo signIn(String account, String password, String path) {// 参数校验AssertUtil.isNotEmpty(account, "请输入登录帐号");AssertUtil.isNotEmpty(password, "请输入登录密码");// 构建请求头HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);// 构建请求体(请求参数)MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();body.add("username", account);body.add("password", password);body.setAll(BeanUtil.beanToMap(clientOAuth2DataConfiguration));HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);// 设置 AuthorizationrestTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(clientOAuth2DataConfiguration.getClientId(),clientOAuth2DataConfiguration.getSecret()));// 发送请求ResponseEntity<ResultInfo> result = restTemplate.postForEntity(oauthServerName + "oauth/token", entity, ResultInfo.class);// 处理返回结果AssertUtil.isTrue(result.getStatusCode() != HttpStatus.OK, "登录失败");ResultInfo resultInfo = result.getBody();if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {// 登录失败resultInfo.setData(resultInfo.getMessage());return resultInfo;}// 这里的 Data 是一个 LinkedHashMap 转成了域对象 OAuthDinerInfoOAuthUserInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap) resultInfo.getData(),new OAuthUserInfo(), false);// 根据业务需求返回视图对象LoginUserInfo loginDinerInfo = new LoginUserInfo();loginDinerInfo.setToken(dinerInfo.getAccessToken());loginDinerInfo.setAvatarUrl(dinerInfo.getAvatarUrl());loginDinerInfo.setNickname(dinerInfo.getNickname());return ResultInfoUtil.buildSuccess(path, loginDinerInfo);}
登录验证:
分别启动注册中心,授权认证中心,用户服务,访问http://localhost:8080/,可以看到授权认证中心和用户微服务都已经注册到eureka:
通过接口请求访问http://localhost:8083/user/signin?account=zjq&password=123456,返回如下:
退出登录
退出登录代码如下:
/*** 安全退出** @param access_token* @param authorization* @return*/@GetMapping("user/logout")public ResultInfo logout(String access_token, String authorization) {// 判断 access_token 是否为空,为空将 authorization 赋值给 access_tokenif (StringUtils.isBlank(access_token)) {access_token = authorization;}// 判断 authorization 是否为空if (StringUtils.isBlank(access_token)) {return ResultInfoUtil.buildSuccess(request.getServletPath(), "退出成功");}// 判断 bearer token 是否为空if (access_token.toLowerCase().contains("bearer ".toLowerCase())) {access_token = access_token.toLowerCase().replace("bearer ", "");}// 清除 redis token 信息OAuth2AccessToken oAuth2AccessToken = redisTokenStore.readAccessToken(access_token);if (oAuth2AccessToken != null) {redisTokenStore.removeAccessToken(oAuth2AccessToken);OAuth2RefreshToken refreshToken = oAuth2AccessToken.getRefreshToken();redisTokenStore.removeRefreshToken(refreshToken);}return ResultInfoUtil.buildSuccess(request.getServletPath(), "退出成功");}
至此,我们已经完成了用户的登录和退出流程。
本文内容到此结束了,
如有收获欢迎点赞spring Cloud微服务 security+oauth2认证授权中心自定义令牌增强,并实现登录和退出相关推荐
- Java之 Spring Cloud 微服务的 SpringCloud Config 配置中心(第四个阶段)【二】【SpringBoot项目实现商品服务器端调用】
SpringCloud学习目录点击跳转对应的文章 Java之 Spring Cloud 微服务搭建(第一个阶段)[一][SpringBoot项目实现商品服务器端是调用] Java之 Spring Cl ...
- (四)spring cloud微服务分布式云架构-配置中心和消息总线(配置中心终结版)...
Spring Cloud Bus Spring cloud bus通过轻量消息代理连接各个分布的节点.这会用在广播状态的变化(例如配置变化)或者其他的消息指令.Spring bus的一个核心思想是通过 ...
- 使用Spring Security 资源服务器来保护Spring Cloud 微服务
我在上一篇对资源服务器进行了简单的阐述,让大家对资源服务器的概念有了简单的认识,今天我将用实际例子来演示单体应用改造为Spring Cloud微服务时的资源服务器实现. 资源服务器改造 以Spring ...
- Spring Cloud微服务下的权限架构调研
随着微服务架构的流行,系统架构调整,项目权限系统模块开发提上日程,需要对权限架构进行设计以及技术选型.所以这段时间看了下相关的资料,做了几个对比选择. 一.架构图 初步设想的架构如下,结构很简单:eu ...
- 企业开发必备的6个Spring Cloud微服务开源项目
前言 今天介绍六款比较热门的SpringCloud微服务项目,感兴趣的可以clone下来研究一下,相信对你学习微服务架构很有帮助.一键获取源码地址 一.Cloud-Platform 介绍 Cloud- ...
- Spring Cloud 微服务开放平台接口
Spring Cloud 微服务开放平台接口 github源码地址: https://github.com/spring-cloud/spring-cloud-security 前言: 什么是开放平台 ...
- 《Spring Cloud 微服务架构进阶》读书笔记
前页 随着 DevOps 和以 Docker 为主的容器技术的发展,云原生应用架构和微服 务变得流行起来. 云原生包含的内容很多,如 DevOps.持续交付.微服务.敏捷等 第一章,微服务架构介绍 架 ...
- spring cloud微服务分布式云架构 - Spring Cloud集成项目简介
Spring Cloud集成项目有很多,下面我们列举一下和Spring Cloud相关的优秀项目,我们的企业架构中用到了很多的优秀项目,说白了,也是站在巨人的肩膀上去整合的.在学习Spring Clo ...
- Spring Cloud微服务分布式云架构—集成项目简介
Spring Cloud集成项目有很多,下面我们列举一下和Spring Cloud相关的优秀项目,我们的企业架构中用到了很多的优秀项目,说白了,也是站在巨人的肩膀上去整合的.在学习Spring Clo ...
最新文章
- OpenCV+python:Canny边缘检测算法
- ASP.NET中Image控件不能自动刷新
- xctf php2,XCTF PHP2
- linux 支持的字体命令,Linux设置显示中文和字体
- android 常用注解,Android 开发小工具之:注解 Annotation
- Sentinel降级_RT_分布式系统集群限流_线程数隔离_削峰填谷_流量控制_速率控制_服务熔断_服务降级---微服务升级_SpringCloud Alibaba工作笔记0039
- Sauce Labs将分析和扩展调试添加到其持续测试云中
- spark--环境搭建--4.ZooKeeper345集群搭建
- Unity 序列帧动画
- MATLAB 2017b 安装教程,这一篇文章就够了。强烈推荐!!!!!
- 均线颜色怎么区分_股票均线颜色
- macbook历代_你都见过吗?苹果Mac历代台式机回忆录
- 论文在线翻译神器-唐帕翻译
- LogSeq 表格合并单元格
- java phaser 实用场景_猿灯塔-Phaser 使用介绍
- 关于七牛云上传图片的总结
- Android——RecyclerView——Recycler类全部源码翻译及注释
- 编码过程中单词常用的缩写方式(转载)
- 美通企业日报 | 亚洲使用护肤品男性稳步增长;新地标BFC外滩金融中心将开业...
- 2012年移动SEO启示
热门文章
- 小草酒店客房管理系统 v2.61 单机/网络版 bt
- Hadoop认证Kerberos--UserGroupInformation.doAs
- 优酷路由宝无线服务器,优酷路由宝一站式刷潘多拉!!!整理版
- 用html和css做失物招领网页,基于Web的高校寻物启事及失物招领平台开发
- 数据挖掘关联分析中的支持度、置信度和提升度
- aspcms错误3706解决方法
- CVPR2022《TransMix: Attend to Mix for Vision Transformers》
- java调用dll报内存溢出错误或者程序崩溃
- 达梦数据库DSC集群搭建
- 华为新款开机+音量上进不去Rec双清模式解决办法