From bfd6ec188b9f174c48516600a3d2eace8f2a5a5f Mon Sep 17 00:00:00 2001 From: sxu <602087911@qq.com> Date: Mon, 19 May 2025 10:29:21 +0800 Subject: [PATCH] OAUTH --- bonus-auth/pom.xml | 5 + .../com/bonus/auth/config/OAuth2Config.java | 67 +++++ .../auth/controller/TicketController.java | 275 ++++++++---------- 3 files changed, 201 insertions(+), 146 deletions(-) create mode 100644 bonus-auth/src/main/java/com/bonus/auth/config/OAuth2Config.java diff --git a/bonus-auth/pom.xml b/bonus-auth/pom.xml index cc21a7d..2129a99 100644 --- a/bonus-auth/pom.xml +++ b/bonus-auth/pom.xml @@ -113,6 +113,11 @@ spring-security-oauth2 2.5.2.RELEASE + + org.springframework.security + spring-security-jwt + 1.1.1.RELEASE + diff --git a/bonus-auth/src/main/java/com/bonus/auth/config/OAuth2Config.java b/bonus-auth/src/main/java/com/bonus/auth/config/OAuth2Config.java new file mode 100644 index 0000000..35313c1 --- /dev/null +++ b/bonus-auth/src/main/java/com/bonus/auth/config/OAuth2Config.java @@ -0,0 +1,67 @@ +package com.bonus.auth.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; +import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; + +@Configuration +@EnableAuthorizationServer +public class OAuth2Config extends AuthorizationServerConfigurerAdapter { + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private PasswordEncoder passwordEncoder; + + // Configure the token store and JWT converter + @Bean + public TokenStore tokenStore() { + return new JwtTokenStore(accessTokenConverter()); + } + + @Bean + public JwtAccessTokenConverter accessTokenConverter() { + JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); + converter.setSigningKey("your-256-bit-secret"); // Use a secure key in production + return converter; + } + + // Configure security constraints on the token endpoint + @Override + public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { + security.tokenKeyAccess("permitAll()") // Public key for token verification + .checkTokenAccess("isAuthenticated()") // Token validation endpoint + .allowFormAuthenticationForClients(); + } + + // Configure client details service + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients.inMemory() + .withClient("client-id") // Your client ID + .secret(passwordEncoder.encode("client-secret")) // Your client secret + .authorizedGrantTypes("password", "refresh_token") + .scopes("read", "write") + .accessTokenValiditySeconds(3600) // 1 hour + .refreshTokenValiditySeconds(86400); // 24 hours + } + + // Configure the endpoints + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { + endpoints.authenticationManager(authenticationManager) + .tokenStore(tokenStore()) + .accessTokenConverter(accessTokenConverter()); + } +} \ No newline at end of file diff --git a/bonus-auth/src/main/java/com/bonus/auth/controller/TicketController.java b/bonus-auth/src/main/java/com/bonus/auth/controller/TicketController.java index 8ec8b76..daca620 100644 --- a/bonus-auth/src/main/java/com/bonus/auth/controller/TicketController.java +++ b/bonus-auth/src/main/java/com/bonus/auth/controller/TicketController.java @@ -1,149 +1,132 @@ -//package com.bonus.auth.controller; +package com.bonus.auth.controller; + +import com.alibaba.nacos.common.utils.UuidUtils; +import com.bonus.common.core.constant.SecurityConstants; +import com.bonus.common.core.utils.encryption.Sm4Utils; +import com.bonus.common.core.web.domain.AjaxResult; +import com.bonus.common.security.utils.SecurityUtils; +import com.bonus.system.api.RemoteUserService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import javax.annotation.Resource; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Slf4j +@RestController +@RequestMapping("/ticket") +public class TicketController { + @Resource + private RemoteUserService remoteUserService; + + @Resource + public RedisTemplate redisTemplate; + + @Resource + private TokenStore tokenStore; + + /** + * 获得用户token、userId、当前时间加密的字符串 + * 跳转第三方菜单时,获取 登录凭证 Ticket + * 将Ticket记录在redis中,设置时效 60s,记录用户id、用户的token、和当前时间 + */ + @GetMapping("getUserTicket") + public String getUserTicket() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication instanceof OAuth2Authentication) { + Object details = authentication.getDetails(); + if (details instanceof OAuth2AuthenticationDetails) { + OAuth2AuthenticationDetails detail = (OAuth2AuthenticationDetails) details; + String tokenValue = detail.getTokenValue(); + String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); + Long userId = SecurityUtils.getUserId(); + String ticket = tokenValue + "," + dateStr + "," + userId; + String encryptedString = Sm4Utils.encrypt(ticket); + if (encryptedString != null) { + String uuid = UuidUtils.generateUuid(); + redisTemplate.opsForValue().set(uuid, encryptedString, 60, TimeUnit.SECONDS); + return uuid; + } + } + } + throw new RuntimeException("未知错误"); + } + + /** + * 登录凭证 Ticket校验 + * 第三方系统拿到Ticket后需要校验该Ticket有效性,校验通过返回用户信息 + * @param ticket:登录凭证 + * @param appId:第三方系统注册颁发的APPID,唯一标识,用来控制第三方系统的接入 + * @return + */ + @GetMapping("validate") + public AjaxResult getUserInfo(String ticket, String appId) { + if (appId==null || "".equals(appId)) { + log.error("第三方系统Ticket校验失败: ticket:{} 结果 :{} ",ticket,"APPID为空"); + return new AjaxResult(10000, "APPID为空!"); + } + if (ticket==null || "".equals(ticket)) { + log.error("第三方系统Ticket校验失败:appId:{} 结果 :{} ",appId,"令牌为空"); + return new AjaxResult(10001, "令牌为空!"); + } + + String encryptedString = redisTemplate.opsForValue().get(ticket); + if (StringUtils.isBlank(encryptedString)) { + log.error("第三方系统Ticket校验失败:appId:{} ticket:{} 结果 :{} ",appId,ticket,"令牌已失效"); + return new AjaxResult(10003, "令牌已失效!"); + } + + + String realTicket = Sm4Utils.decrypt(encryptedString); + if (StringUtils.isBlank(realTicket)) { + log.error("第三方系统Ticket校验失败:appId:{} ticket:{} 结果 :{} ",appId,ticket,"令牌解析错误"); + return new AjaxResult(10004, "令牌解析错误!"); + } + String[] ticketInfoArr = realTicket.split(","); + + String tokenValue = ticketInfoArr[0]; + OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue); + if (accessToken == null || StringUtils.isEmpty(accessToken.getValue())) { + log.error("第三方系统Ticket校验失败:appId:{} ticket:{} 结果 :{} ",appId,ticket,"当前用户已离线,请重新登录"); + return new AjaxResult(10005, "当前用户已离线,请重新登录!"); + } + String userId = ticketInfoArr[2]; + AjaxResult result = remoteUserService.getInfo(Long.parseLong(userId), SecurityConstants.INNER); + + +// Map resMap = new HashMap<>(); +// resMap.put("userName", sysUser.getUserName()); +// resMap.put("name", sysUser.getNickName()); +// resMap.put("deptId", sysUser.getDeptId()); +// resMap.put("deptName", sysUser.getDept() == null ? null : sysUser.getDept().getDeptName().replaceAll("YJ", "")); // -//import com.alibaba.nacos.common.utils.UuidUtils; -//import com.bonus.common.core.constant.SecurityConstants; -//import com.bonus.common.core.utils.encryption.Sm4Utils; -//import com.bonus.common.core.web.domain.AjaxResult; -//import com.bonus.common.security.utils.SecurityUtils; -//import com.bonus.system.api.RemoteUserService; -//import com.bonus.system.api.domain.SysDept; -//import com.bonus.system.api.domain.SysUser; -//import lombok.extern.slf4j.Slf4j; -//import org.apache.commons.lang3.StringUtils; -//import org.springframework.data.redis.core.RedisTemplate; -//import org.springframework.security.core.Authentication; -//import org.springframework.security.core.context.SecurityContextHolder; -//import org.springframework.security.oauth2.common.OAuth2AccessToken; -//import org.springframework.security.oauth2.provider.OAuth2Authentication; -//import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; -//import org.springframework.security.oauth2.provider.token.TokenStore; -//import org.springframework.web.bind.annotation.GetMapping; -//import org.springframework.web.bind.annotation.RequestMapping; -//import org.springframework.web.bind.annotation.RestController; -//import javax.annotation.Resource; -//import java.text.SimpleDateFormat; -//import java.util.Date; -//import java.util.HashMap; -//import java.util.Map; -//import java.util.concurrent.TimeUnit; -// -///** -// * 第三方系统接入 -// * @author semdo -// */ -//@Slf4j -//@RestController -//@RequestMapping("/ticket") -//public class TicketController { -// @Resource -// private RemoteUserService remoteUserService; -// -// @Resource -// public RedisTemplate redisTemplate; -// -// @Resource -// private TokenStore tokenStore; -// -// @Resource -// private ISysThirdClientAccreditService thirdClientAccreditService; -// -// -// /** -// * 获得用户token、userId、当前时间加密的字符串 -// * 跳转第三方菜单时,获取 登录凭证 Ticket -// * 将Ticket记录在redis中,设置时效 60s,记录用户id、用户的token、和当前时间 -// */ -// @GetMapping("getUserTicket") -// public String getUserTicket() { -// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); -// if (authentication instanceof OAuth2Authentication) { -// Object details = authentication.getDetails(); -// if (details instanceof OAuth2AuthenticationDetails) { -// OAuth2AuthenticationDetails detail = (OAuth2AuthenticationDetails) details; -// String tokenValue = detail.getTokenValue(); -// String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); -// Long userId = SecurityUtils.getUserId(); -// String ticket = tokenValue + "," + dateStr + "," + userId; -// String encryptedString = Sm4Utils.encrypt(ticket); -// if (encryptedString != null) { -// String uuid = UuidUtils.generateUuid(); -// redisTemplate.opsForValue().set(uuid, encryptedString, 60, TimeUnit.SECONDS); -// return uuid; -// } +// SysDept dept = sysUser.getDept(); + // 特定的业务需求,需要记录当前用户是否为运检站,并且返回 xx站 +// if (dept != null) { +// if ("4".equals(dept.getDeptType()) && dept.getDeptName().contains("站")) { +// resMap.put("businessDeptName", dept.getDeptName().replaceAll("YJ", "")); +// } else { +// resMap.put("businessDeptName", ""); // } +// } else { +// resMap.put("businessDeptName", ""); // } -// throw new RuntimeException("未知错误"); -// } -// -// /** -// * 登录凭证 Ticket校验 -// * 第三方系统拿到Ticket后需要校验该Ticket有效性,校验通过返回用户信息 -// * @param ticket:登录凭证 -// * @param appId:第三方系统注册颁发的APPID,唯一标识,用来控制第三方系统的接入 -// * @return -// */ -// @GetMapping("validate") -// public AjaxResult getUserInfo(String ticket, String appId) { -// if (appId==null || "".equals(appId)) { -// log.error("第三方系统Ticket校验失败: ticket:{} 结果 :{} ",ticket,"APPID为空"); -// return new AjaxResult(10000, "APPID为空!"); -// } -// if (ticket==null || "".equals(ticket)) { -// log.error("第三方系统Ticket校验失败:appId:{} 结果 :{} ",appId,"令牌为空"); -// return new AjaxResult(10001, "令牌为空!"); -// } -// boolean appStatus = thirdClientAccreditService.getAppStatusByAppId(appId); -// -// if (!appStatus) { -// log.error("第三方系统Ticket校验失败:appId:{} ticket:{} 结果 :{} ",appId,ticket,"应用不可用"); -// return new AjaxResult(10002, "应用不可用!"); -// } -// -// -// String encryptedString = redisTemplate.opsForValue().get(ticket); -// if (StringUtils.isBlank(encryptedString)) { -// log.error("第三方系统Ticket校验失败:appId:{} ticket:{} 结果 :{} ",appId,ticket,"令牌已失效"); -// return new AjaxResult(10003, "令牌已失效!"); -// } -// -// -// String realTicket = Sm4Utils.decrypt(encryptedString); -// if (StringUtils.isBlank(realTicket)) { -// log.error("第三方系统Ticket校验失败:appId:{} ticket:{} 结果 :{} ",appId,ticket,"令牌解析错误"); -// return new AjaxResult(10004, "令牌解析错误!"); -// } -// String[] ticketInfoArr = realTicket.split(","); -// -// String tokenValue = ticketInfoArr[0]; -// OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue); -// if (accessToken == null || StringUtils.isEmpty(accessToken.getValue())) { -// log.error("第三方系统Ticket校验失败:appId:{} ticket:{} 结果 :{} ",appId,ticket,"当前用户已离线,请重新登录"); -// return new AjaxResult(10005, "当前用户已离线,请重新登录!"); -// } -// String userId = ticketInfoArr[2]; -// AjaxResult result = remoteUserService.getInfo(Long.parseLong(userId), SecurityConstants.INNER); -// -// -//// Map resMap = new HashMap<>(); -//// resMap.put("userName", sysUser.getUserName()); -//// resMap.put("name", sysUser.getNickName()); -//// resMap.put("deptId", sysUser.getDeptId()); -//// resMap.put("deptName", sysUser.getDept() == null ? null : sysUser.getDept().getDeptName().replaceAll("YJ", "")); -//// -//// SysDept dept = sysUser.getDept(); -// // 特定的业务需求,需要记录当前用户是否为运检站,并且返回 xx站 -//// if (dept != null) { -//// if ("4".equals(dept.getDeptType()) && dept.getDeptName().contains("站")) { -//// resMap.put("businessDeptName", dept.getDeptName().replaceAll("YJ", "")); -//// } else { -//// resMap.put("businessDeptName", ""); -//// } -//// } else { -//// resMap.put("businessDeptName", ""); -//// } -// -// log.info("第三方系统Ticket校验成功:appId:{} ticket:{} Ticket生成时间:{}",appId,ticket,ticketInfoArr[1]); -// return AjaxResult.success(result); -// } -// -//} + + log.info("第三方系统Ticket校验成功:appId:{} ticket:{} Ticket生成时间:{}",appId,ticket,ticketInfoArr[1]); + return AjaxResult.success(result); + } + +}