mirror of
https://gitee.com/log4j/pig.git
synced 2024-12-22 04:47:10 +08:00
feat(auth): 单体模式下支持 授权码模式
http://localhost:9999/admin/oauth2/authorize?scope=server&client_id=test&response_type=code&redirect_uri=https://pig4cloud.com
This commit is contained in:
parent
2ff0c87cdd
commit
90fac31a24
@ -38,7 +38,7 @@ CREATE TABLE `config_info` (
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (1, 'application-dev.yml', 'DEFAULT_GROUP', '# 配置文件加密根密码\njasypt:\n encryptor:\n password: pig\n algorithm: PBEWithMD5AndDES\n iv-generator-classname: org.jasypt.iv.NoIvGenerator\n \n# Spring 相关\nspring:\n cache:\n type: redis\n data:\n redis:\n host: pig-redis\n cloud:\n sentinel:\n eager: true\n transport:\n dashboard: pig-sentinel:5003\n openfeign:\n sentinel:\n enabled: true\n okhttp:\n enabled: true\n httpclient:\n enabled: false\n compression:\n request:\n enabled: true\n response:\n enabled: true\n\n# 暴露监控端点\nmanagement:\n endpoints:\n web:\n exposure:\n include: \"*\" \n endpoint:\n health:\n show-details: ALWAYS\n\n# mybaits-plus配置\nmybatis-plus:\n mapper-locations: classpath:/mapper/*Mapper.xml\n global-config:\n banner: false\n db-config:\n id-type: auto\n table-underline: true\n logic-delete-value: 1\n logic-not-delete-value: 0\n type-handlers-package: com.pig4cloud.pig.common.mybatis.handler\n configuration:\n map-underscore-to-camel-case: true\n shrink-whitespaces-in-sql: true\n\n# swagger 配置\nswagger:\n enabled: true\n title: Pig Swagger API\n gateway: http://${GATEWAY_HOST:pig-gateway}:${GATEWAY-PORT:9999}\n token-url: ${swagger.gateway}/auth/oauth2/token\n scope: server\n services:\n pig-upms-biz: admin\n pig-codegen: gen', 'e8b519db39e79d600a957cf47b3306d5', '2022-05-08 12:10:37', '2024-03-28 14:05:12', 'nacos', '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', '');
|
||||
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (2, 'pig-auth-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n freemarker:\n allow-request-override: false\n allow-session-override: false\n cache: true\n charset: UTF-8\n check-template-location: true\n content-type: text/html\n enabled: true\n expose-request-attributes: false\n expose-session-attributes: false\n expose-spring-macro-helpers: true\n prefer-file-system-access: true\n suffix: .ftl\n template-loader-path: classpath:/templates/\n\n\nsecurity:\n encode-key: \'thanks,pig4cloud\'\n ignore-clients: \n - test', '1d14791d3c4c153acd3bee55fc114bd6', '2022-05-08 12:10:37', '2024-04-04 21:40:47', 'nacos', '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', '');
|
||||
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (2, 'pig-auth-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n freemarker:\n allow-request-override: false\n allow-session-override: false\n cache: true\n charset: UTF-8\n check-template-location: true\n content-type: text/html\n enabled: true\n request-context-attribute: request\n expose-request-attributes: false\n expose-session-attributes: false\n expose-spring-macro-helpers: true\n prefer-file-system-access: true\n suffix: .ftl\n template-loader-path: classpath:/templates/\n\n\nsecurity:\n encode-key: \'thanks,pig4cloud\'\n ignore-clients:\n - test\n - client\n - open\n - app', 'b4a660ece61e8180b4940a0770eddfee', '2022-05-08 12:10:37', '2024-12-14 23:56:48', 'nacos', '127.0.0.1', '', '', '', '', '', 'yaml', '', '');
|
||||
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (3, 'pig-codegen-dev.yml', 'DEFAULT_GROUP', '# 数据源配置\nspring:\n datasource:\n type: com.zaxxer.hikari.HikariDataSource\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: root\n password: root\n url: jdbc:mysql://pig-mysql:3306/pig?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true\n resources:\n static-locations: classpath:/static/,classpath:/views/\n', 'cf786dbe3b07074fc187bf2eab3266b1', '2022-05-08 12:10:37', '2023-01-28 14:05:36', '', '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', '');
|
||||
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (4, 'pig-gateway-dev.yml', 'DEFAULT_GROUP', 'spring:\n cloud:\n gateway:\n locator:\n enabled: true\n routes:\n # 认证中心\n - id: pig-auth\n uri: lb://pig-auth\n predicates:\n - Path=/auth/**\n #UPMS 模块\n - id: pig-upms-biz\n uri: lb://pig-upms-biz\n predicates:\n - Path=/admin/**\n filters:\n # 限流配置\n - name: RequestRateLimiter\n args:\n key-resolver: \'#{@remoteAddrKeyResolver}\'\n redis-rate-limiter.replenishRate: 100\n redis-rate-limiter.burstCapacity: 200\n # 代码生成模块\n - id: pig-codegen\n uri: lb://pig-codegen\n predicates:\n - Path=/gen/**\n # 代码生成模块\n - id: pig-quartz\n uri: lb://pig-quartz\n predicates:\n - Path=/job/**\n # 固定路由转发配置 无修改\n - id: openapi\n uri: lb://pig-gateway\n predicates:\n - Path=/v3/api-docs/**\n filters:\n - RewritePath=/v3/api-docs/(?<path>.*), /$\\{path}/$\\{path}/v3/api-docs', '7940b3e89a9489e0844af6b1dc91d65b', '2022-05-08 12:10:37', '2024-04-04 21:40:32', 'nacos', '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', '');
|
||||
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (5, 'pig-monitor-dev.yml', 'DEFAULT_GROUP', 'spring:\n autoconfigure:\n exclude: com.pig4cloud.pig.common.core.config.JacksonConfiguration\n # 安全配置\n security:\n user:\n name: ENC(8Hk2ILNJM8UTOuW/Xi75qg==) # pig\n password: ENC(o6cuPFfUevmTbkmBnE67Ow====) # pig\n', '650bdfa15f60f3faa84dfe6e6878b8cf', '2022-05-08 12:10:37', '2022-05-08 12:10:37', NULL, '127.0.0.1', '', '', NULL, NULL, NULL, 'yaml', NULL, '');
|
||||
|
@ -58,7 +58,7 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<!--freemarker-->
|
||||
<!--freemarker 授权码模式渲染-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
|
@ -30,7 +30,6 @@ import com.pig4cloud.pig.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationCo
|
||||
import com.pig4cloud.pig.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationProvider;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
@ -44,12 +43,15 @@ import org.springframework.security.oauth2.server.authorization.settings.Authori
|
||||
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.*;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@ -69,10 +71,17 @@ public class AuthorizationServerConfiguration {
|
||||
|
||||
private final ValidateCodeFilter validateCodeFilter;
|
||||
|
||||
/**
|
||||
* Authorization Server 配置,仅对 /oauth2/** 的请求有效
|
||||
* @param http http
|
||||
* @return {@link SecurityFilterChain }
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@ConditionalOnProperty(value = "security.micro", matchIfMissing = true)
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
public SecurityFilterChain authorizationServer(HttpSecurity http) throws Exception {
|
||||
// 配置授权服务器的安全策略,只有/oauth2/**的请求才会走如下的配置
|
||||
http.securityMatcher("/oauth2/**");
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
|
||||
|
||||
// 增加验证码过滤器
|
||||
@ -87,22 +96,16 @@ public class AuthorizationServerConfiguration {
|
||||
}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证
|
||||
oAuth2ClientAuthenticationConfigurer.errorResponseHandler(new PigAuthenticationFailureEventHandler()))// 处理客户端认证异常
|
||||
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面
|
||||
.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults());
|
||||
.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults())
|
||||
.authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated());
|
||||
|
||||
AntPathRequestMatcher[] requestMatchers = new AntPathRequestMatcher[] {
|
||||
AntPathRequestMatcher.antMatcher("/token/**"), AntPathRequestMatcher.antMatcher("/actuator/**"),
|
||||
AntPathRequestMatcher.antMatcher("/code/image"), AntPathRequestMatcher.antMatcher("/css/**"),
|
||||
AntPathRequestMatcher.antMatcher("/error") };
|
||||
// 设置 Token 存储的策略
|
||||
http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder().issuer(SecurityConstants.PROJECT_LICENSE).build()),
|
||||
Customizer.withDefaults());
|
||||
|
||||
http.authorizeHttpRequests(authorizeRequests -> {
|
||||
// 自定义接口、端点暴露
|
||||
authorizeRequests.requestMatchers(requestMatchers).permitAll();
|
||||
authorizeRequests.anyRequest().authenticated();
|
||||
})
|
||||
.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder().issuer(SecurityConstants.PROJECT_LICENSE).build()),
|
||||
Customizer.withDefaults());
|
||||
// 设置授权码模式登录页面
|
||||
http.with(new FormIdentityLoginConfigurer(), Customizer.withDefaults());
|
||||
DefaultSecurityFilterChain securityFilterChain = http.build();
|
||||
|
||||
|
@ -73,8 +73,8 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/token")
|
||||
public class PigTokenEndpoint {
|
||||
|
||||
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
|
||||
@ -90,19 +90,23 @@ public class PigTokenEndpoint {
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
/**
|
||||
* 认证页面
|
||||
* 授权码模式:认证页面
|
||||
* @param modelAndView
|
||||
* @param error 表单登录失败处理回调的错误信息
|
||||
* @return ModelAndView
|
||||
*/
|
||||
@GetMapping("/login")
|
||||
@GetMapping("/token/login")
|
||||
public ModelAndView require(ModelAndView modelAndView, @RequestParam(required = false) String error) {
|
||||
modelAndView.setViewName("ftl/login");
|
||||
modelAndView.addObject("error", error);
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
@GetMapping("/confirm_access")
|
||||
/**
|
||||
* 授权码模式:确认页面
|
||||
* @return {@link ModelAndView }
|
||||
*/
|
||||
@GetMapping("/oauth2/confirm_access")
|
||||
public ModelAndView confirm(Principal principal, ModelAndView modelAndView,
|
||||
@RequestParam(OAuth2ParameterNames.CLIENT_ID) String clientId,
|
||||
@RequestParam(OAuth2ParameterNames.SCOPE) String scope,
|
||||
@ -121,10 +125,11 @@ public class PigTokenEndpoint {
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出并删除token
|
||||
* @param authHeader Authorization
|
||||
* 注销并删除令牌
|
||||
* @param authHeader auth 标头
|
||||
* @return {@link R }<{@link Boolean }>
|
||||
*/
|
||||
@DeleteMapping("/logout")
|
||||
@DeleteMapping("/token/logout")
|
||||
public R<Boolean> logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) {
|
||||
if (StrUtil.isBlank(authHeader)) {
|
||||
return R.ok();
|
||||
@ -135,11 +140,13 @@ public class PigTokenEndpoint {
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验token
|
||||
* @param token 令牌
|
||||
* 检查令牌
|
||||
* @param token 令 牌
|
||||
* @param response 响应
|
||||
* @param request 请求
|
||||
*/
|
||||
@SneakyThrows
|
||||
@GetMapping("/check_token")
|
||||
@GetMapping("/token/check_token")
|
||||
public void checkToken(String token, HttpServletResponse response, HttpServletRequest request) {
|
||||
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
|
||||
|
||||
@ -165,11 +172,12 @@ public class PigTokenEndpoint {
|
||||
}
|
||||
|
||||
/**
|
||||
* 令牌管理调用
|
||||
* @param token token
|
||||
* 删除令牌
|
||||
* @param token 令 牌
|
||||
* @return {@link R }<{@link Boolean }>
|
||||
*/
|
||||
@Inner
|
||||
@DeleteMapping("/remove/{token}")
|
||||
@DeleteMapping("/token/remove/{token}")
|
||||
public R<Boolean> removeToken(@PathVariable("token") String token) {
|
||||
OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
|
||||
if (authorization == null) {
|
||||
@ -191,12 +199,12 @@ public class PigTokenEndpoint {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询token
|
||||
* @param params 分页参数
|
||||
* @return
|
||||
* 令牌列表
|
||||
* @param params 参数
|
||||
* @return {@link R }<{@link Page }>
|
||||
*/
|
||||
@Inner
|
||||
@PostMapping("/page")
|
||||
@PostMapping("/token/page")
|
||||
public R<Page> tokenList(@RequestBody Map<String, Object> params) {
|
||||
// 根据分页参数获取对应数据
|
||||
String key = String.format("%s::*", CacheConstants.PROJECT_OAUTH_ACCESS);
|
||||
|
@ -18,11 +18,12 @@ public final class FormIdentityLoginConfigurer
|
||||
public void init(HttpSecurity http) throws Exception {
|
||||
http.formLogin(formLogin -> {
|
||||
formLogin.loginPage("/token/login");
|
||||
formLogin.loginProcessingUrl("/token/form");
|
||||
formLogin.loginProcessingUrl("/oauth2/form");
|
||||
formLogin.failureHandler(new FormAuthenticationFailureHandler());
|
||||
|
||||
})
|
||||
.logout(logout -> logout.logoutSuccessHandler(new SsoLogoutSuccessHandler())
|
||||
.logout(logout -> logout.logoutUrl("/oauth2/logout")
|
||||
.logoutSuccessHandler(new SsoLogoutSuccessHandler())
|
||||
.deleteCookies("JSESSIONID")
|
||||
.invalidateHttpSession(true)) // SSO登出成功处理
|
||||
|
||||
|
@ -60,9 +60,9 @@ public class ValidateCodeFilter extends OncePerRequestFilter {
|
||||
return;
|
||||
}
|
||||
|
||||
// 客户端配置跳过验证码
|
||||
// 如果是密码模式 && 客户端不需要校验验证码
|
||||
boolean isIgnoreClient = authSecurityConfigProperties.getIgnoreClients().contains(WebUtils.getClientId());
|
||||
if (isIgnoreClient) {
|
||||
if (StrUtil.equals(SecurityConstants.PASSWORD, grantType) && isIgnoreClient) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2022-06-02
|
||||
@ -46,9 +48,21 @@ public class FormAuthenticationFailureHandler implements AuthenticationFailureHa
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) {
|
||||
log.debug("表单登录失败:{}", exception.getLocalizedMessage());
|
||||
String url = HttpUtil.encodeParams(String.format("/token/login?error=%s", exception.getMessage()),
|
||||
|
||||
// 获取当前请求的context-path
|
||||
String contextPath = request.getContextPath();
|
||||
|
||||
// 构建重定向URL,加入context-path
|
||||
String url = HttpUtil.encodeParams(
|
||||
String.format("%s/token/login?error=%s", contextPath, exception.getMessage()),
|
||||
CharsetUtil.CHARSET_UTF_8);
|
||||
WebUtils.getResponse().sendRedirect(url);
|
||||
|
||||
try {
|
||||
WebUtils.getResponse().sendRedirect(url);
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("重定向失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,117 +1,52 @@
|
||||
<#assign content>
|
||||
<div class="mb-7">
|
||||
<h3 class="font-semibold text-2xl text-gray-800 text-center">应用授权确认</h3>
|
||||
<div class="mt-4 flex justify-between text-sm text-gray-600">
|
||||
<div>
|
||||
<#if principalName=="anonymousUser">
|
||||
未登录
|
||||
<#else>
|
||||
<a href="https://pig4cloud.com" class="hover:text-purple-600">${principalName}</a>
|
||||
</#if>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://pig4cloud.com" class="hover:text-purple-600">技术支持</a>
|
||||
<h3 class="font-semibold text-2xl text-gray-800 dark:text-gray-200 text-center transition-colors">应用授权确认</h3>
|
||||
<div class="mt-4 flex items-center justify-center text-sm">
|
||||
<div class="px-4 py-2 bg-gray-50 dark:bg-gray-800 rounded-full transition-all">
|
||||
<div class="text-gray-700 dark:text-gray-300">
|
||||
<#if principalName=="anonymousUser">
|
||||
<span class="text-gray-500 dark:text-gray-400">未登录用户</span>
|
||||
<#else>
|
||||
<a href="https://pig4cloud.com" class="text-purple-600 dark:text-purple-400 hover:text-purple-700 dark:hover:text-purple-300 font-medium transition-colors">
|
||||
${principalName}
|
||||
</a>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id='confirmationForm' name='confirmationForm' action="/oauth2/authorize" method='post'>
|
||||
<form id='confirmationForm' name='confirmationForm' action="${request.contextPath}/oauth2/authorize" method='post'>
|
||||
<input type="hidden" name="client_id" value="${clientId}">
|
||||
<input type="hidden" name="state" value="${state}">
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="mb-4">
|
||||
<p class="text-gray-700 mb-3">将获得以下权限:</p>
|
||||
<div class="space-y-3 bg-gray-50 p-4 rounded-lg">
|
||||
<#list scopeList as scope>
|
||||
<p class="text-gray-700 dark:text-gray-300 mb-3 transition-colors">将获得以下权限:</p>
|
||||
<div class="space-y-3 bg-gray-50 dark:bg-gray-800 p-4 rounded-lg border border-gray-200 dark:border-gray-700 transition-all">
|
||||
<#list scopeList as scope>
|
||||
<div class="flex items-center">
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport"
|
||||
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
|
||||
<title>Pig 第三方授权</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="bg-purple-900 absolute top-0 left-0 bg-gradient-to-b from-gray-900 via-gray-900 to-purple-800 bottom-0 leading-5 h-full w-full overflow-hidden">
|
||||
</div>
|
||||
|
||||
<div class="relative min-h-screen sm:flex sm:flex-row justify-center bg-transparent rounded-3xl shadow-xl">
|
||||
<div class="flex-col flex self-center lg:px-14 sm:max-w-4xl xl:max-w-md z-10">
|
||||
<div class="self-start hidden lg:flex flex-col text-gray-300">
|
||||
<p class="pr-3 text-sm opacity-75">
|
||||
为企业提供一套集中式的账号、权限、认证、审计工具,帮助企业打通身份数据孤岛,实现"一个账号、一次认证、多点通行"的效果,强化企业安全体系的同时,提升组织管理效率,助力企业数字化升级转型。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center self-center z-10">
|
||||
<div class="p-12 bg-white mx-auto rounded-3xl w-96">
|
||||
<div class="mb-7">
|
||||
<h3 class="font-semibold text-2xl text-gray-800 text-center">应用授权确认</h3>
|
||||
<div class="mt-4 flex justify-between text-sm text-gray-600">
|
||||
<div>
|
||||
<#if principalName=="anonymousUser">
|
||||
未登录
|
||||
<#else>
|
||||
<a href="https://pig4cloud.com" class="hover:text-purple-600">${principalName}</a>
|
||||
</#if>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://pig4cloud.com" class="hover:text-purple-600">技术支持</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id='confirmationForm' name='confirmationForm' action="/oauth2/authorize" method='post'>
|
||||
<input type="hidden" name="client_id" value="${clientId}">
|
||||
<input type="hidden" name="state" value="${state}">
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="mb-4">
|
||||
<p class="text-gray-700 mb-3">将获得以下权限:</p>
|
||||
<div class="space-y-3 bg-gray-50 p-4 rounded-lg">
|
||||
<#list scopeList as scope>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox" checked="checked" name="scope" value="${scope}"
|
||||
class="h-4 w-4 text-purple-600 focus:ring-purple-500 border-gray-300 rounded">
|
||||
<label class="ml-3 text-gray-600">${scope}</label>
|
||||
</div>
|
||||
</#list>
|
||||
<input type="checkbox" checked="checked" name="scope" value="${scope}"
|
||||
class="h-4 w-4 text-purple-600 focus:ring-purple-500 dark:focus:ring-purple-400 border-gray-300 dark:border-gray-600 rounded transition-colors">
|
||||
<label class="ml-3 text-gray-600 dark:text-gray-400 transition-colors">${scope}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-gray-500 mb-4">
|
||||
授权后表明你已同意
|
||||
<a href="#" class="text-purple-600 hover:text-purple-700">服务协议</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="submit" id="write-email-btn"
|
||||
class="w-full flex justify-center bg-purple-800 hover:bg-purple-700 text-gray-100 p-3 rounded-lg tracking-wide font-semibold cursor-pointer transition ease-in duration-500">
|
||||
确认授权
|
||||
</button>
|
||||
</div>
|
||||
</#list>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="mt-7 text-center text-gray-300 text-xs">
|
||||
<span>
|
||||
Copyright © 2021-2025
|
||||
<a href="#" class="text-purple-500 hover:text-purple-600">PIGCLOUD</a>
|
||||
</span>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400 mb-4 transition-colors">
|
||||
授权后表明你已同意
|
||||
<a href="#" class="text-purple-600 dark:text-purple-400 hover:text-purple-700 dark:hover:text-purple-300 transition-colors">服务协议</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="submit" id="write-email-btn"
|
||||
class="w-full flex justify-center bg-purple-600 hover:bg-purple-700 dark:bg-purple-700 dark:hover:bg-purple-600 text-gray-100 p-3 rounded-lg tracking-wide font-semibold cursor-pointer transition-all duration-300 transform hover:scale-[1.02]">
|
||||
确认授权
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</#assign>
|
||||
|
||||
<svg class="absolute bottom-0 left-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320">
|
||||
<path fill="#fff" fill-opacity="1"
|
||||
d="M0,0L40,42.7C80,85,160,171,240,197.3C320,224,400,192,480,154.7C560,117,640,75,720,74.7C800,75,880,117,960,154.7C1040,192,1120,224,1200,213.3C1280,203,1360,149,1400,122.7L1440,96L1440,320L1400,320C1360,320,1280,320,1200,320C1120,320,1040,320,960,320C880,320,800,320,720,320C640,320,560,320,480,320C400,320,320,320,240,320C160,320,80,320,40,320L0,320Z">
|
||||
</path>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
<#include "layout/base.ftl">
|
||||
|
@ -1,37 +1,75 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<html class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><#if title??>${title}<#else>Pig 统一身份认证</#if></title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#6366f1'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.dark-mode-toggle {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.dark-mode-toggle:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.animate-spin-slow {
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
</style>
|
||||
<#if extraHead??>${extraHead}</#if>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 背景渐变 -->
|
||||
<div class="bg-purple-900 absolute top-0 left-0 bg-gradient-to-b from-gray-900 via-gray-900 to-purple-800 bottom-0 leading-5 h-full w-full overflow-hidden">
|
||||
<body class="min-h-screen">
|
||||
<!-- 暗黑模式切换按钮 -->
|
||||
<div class="fixed top-6 right-6 z-50">
|
||||
<button id="theme-toggle" class="dark-mode-toggle p-3 rounded-xl bg-purple-100/90 dark:bg-gray-800/90 text-purple-600 dark:text-yellow-300 hover:bg-purple-200 dark:hover:bg-gray-700 focus:outline-none transition-all duration-300 shadow-lg">
|
||||
<!-- 月亮图标 -->
|
||||
<svg id="moon-icon" class="w-6 h-6 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
|
||||
</svg>
|
||||
<!-- 太阳图标 -->
|
||||
<svg id="sun-icon" class="w-6 h-6 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="relative min-h-screen sm:flex sm:flex-row justify-center bg-transparent rounded-3xl shadow-xl">
|
||||
<!-- 背景渐变 -->
|
||||
<div class="bg-purple-900 fixed top-0 left-0 bg-gradient-to-b from-gray-900 via-gray-900 to-purple-800 w-full h-full -z-10">
|
||||
</div>
|
||||
|
||||
<div class="relative min-h-screen flex flex-col md:flex-row items-center justify-center px-4 py-12 md:px-8">
|
||||
<!-- 左侧说明文本 -->
|
||||
<div class="flex-col flex self-center lg:px-14 sm:max-w-4xl xl:max-w-md z-10">
|
||||
<div class="self-start hidden lg:flex flex-col text-gray-300">
|
||||
<p class="pr-3 text-sm opacity-75">
|
||||
<div class="flex-1 max-w-xl mb-8 md:mb-0 md:mr-12 z-10">
|
||||
<div class="hidden md:block">
|
||||
<h2 class="text-3xl font-bold text-white mb-6">统一身份认证平台</h2>
|
||||
<p class="text-gray-300 text-lg leading-relaxed opacity-90">
|
||||
为企业提供一套集中式的账号、权限、认证、审计工具,帮助企业打通身份数据孤岛,实现"一个账号、一次认证、多点通行"的效果,强化企业安全体系的同时,提升组织管理效率,助力企业数字化升级转型。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容区 -->
|
||||
<div class="flex justify-center self-center z-10">
|
||||
<div class="p-12 bg-white mx-auto rounded-3xl w-96">
|
||||
<div class="flex-1 w-full max-w-md z-10">
|
||||
<div class="p-8 md:p-10 bg-white dark:bg-gray-900 rounded-2xl shadow-2xl backdrop-blur-sm transition-all duration-300">
|
||||
<#if content??>${content}</#if>
|
||||
|
||||
<!-- 版权信息 -->
|
||||
<div class="mt-7 text-center text-gray-300 text-xs">
|
||||
<div class="mt-8 pt-6 text-center text-gray-400 dark:text-gray-500 text-sm border-t border-gray-100 dark:border-gray-800">
|
||||
<span>
|
||||
Copyright © 2021-2025
|
||||
<a href="#" class="text-purple-500 hover:text-purple-600">PIGCLOUD</a>
|
||||
<a href="#" class="text-purple-600 dark:text-purple-400 hover:text-purple-700 dark:hover:text-purple-300 transition-colors">PIGCLOUD</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -39,10 +77,41 @@
|
||||
</div>
|
||||
|
||||
<!-- 底部波浪装饰 -->
|
||||
<svg class="absolute bottom-0 left-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320">
|
||||
<path fill="#fff" fill-opacity="1"
|
||||
<svg class="fixed bottom-0 left-0 w-full transition-colors -z-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320">
|
||||
<path class="fill-white dark:fill-gray-900 transition-colors"
|
||||
d="M0,0L40,42.7C80,85,160,171,240,197.3C320,224,400,192,480,154.7C560,117,640,75,720,74.7C800,75,880,117,960,154.7C1040,192,1120,224,1200,213.3C1280,203,1360,149,1400,122.7L1440,96L1440,320L1400,320C1360,320,1280,320,1200,320C1120,320,1040,320,960,320C880,320,800,320,720,320C640,320,560,320,480,320C400,320,320,320,240,320C160,320,80,320,40,320L0,320Z">
|
||||
</path>
|
||||
</svg>
|
||||
|
||||
<script>
|
||||
// 主题切换功能
|
||||
const themeToggle = document.getElementById('theme-toggle');
|
||||
const html = document.documentElement;
|
||||
|
||||
// 检查本地存储中的主题设置
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
html.classList.add('dark');
|
||||
} else {
|
||||
html.classList.remove('dark');
|
||||
}
|
||||
|
||||
// 切换主题
|
||||
themeToggle.addEventListener('click', () => {
|
||||
html.classList.toggle('dark');
|
||||
|
||||
// 保存主题设置到本地存储
|
||||
if (html.classList.contains('dark')) {
|
||||
localStorage.theme = 'dark';
|
||||
} else {
|
||||
localStorage.theme = 'light';
|
||||
}
|
||||
|
||||
// 添加点击动画效果
|
||||
themeToggle.classList.add('animate-spin-slow');
|
||||
setTimeout(() => {
|
||||
themeToggle.classList.remove('animate-spin-slow');
|
||||
}, 300);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,31 +1,36 @@
|
||||
<#assign content>
|
||||
<div class="mb-7">
|
||||
<h3 class="font-semibold text-2xl text-gray-800 text-center">统一身份平台</h3>
|
||||
<div class="mb-8 text-center">
|
||||
<svg class="w-16 h-16 mx-auto mb-4 text-purple-600 dark:text-purple-400" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 14.5V16.5M7 10.0288C7.47142 10.0288 7.86284 9.63734 7.86284 9.16592C7.86284 8.69449 7.47142 8.30307 7 8.30307C6.52858 8.30307 6.13716 8.69449 6.13716 9.16592C6.13716 9.63734 6.52858 10.0288 7 10.0288ZM17 10.0288C17.4714 10.0288 17.8628 9.63734 17.8628 9.16592C17.8628 8.69449 17.4714 8.30307 17 8.30307C16.5286 8.30307 16.1372 8.69449 16.1372 9.16592C16.1372 9.63734 16.5286 10.0288 17 10.0288ZM12 12.5C13.6569 12.5 15 11.1569 15 9.5C15 7.84315 13.6569 6.5 12 6.5C10.3431 6.5 9 7.84315 9 9.5C9 11.1569 10.3431 12.5 12 12.5Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M17.6972 19.7C16.0993 18.0307 14.125 17 12 17C9.87499 17 7.90072 18.0307 6.30283 19.7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">安全便捷的企业级认证服务</p>
|
||||
</div>
|
||||
|
||||
<form class="form-signin" action="/oauth2/form" method="post">
|
||||
|
||||
<form class="form-signin" action="${request.contextPath}/oauth2/form" method="post">
|
||||
<input type="hidden" name="client_id" value="pig">
|
||||
<input type="hidden" name="grant_type" value="password">
|
||||
<div class="space-y-6">
|
||||
<div class="">
|
||||
<input class="w-full text-sm px-4 py-3 bg-gray-200 focus:bg-gray-100 border border-gray-200 rounded-lg focus:outline-none focus:border-purple-400"
|
||||
<input class="w-full text-sm px-4 py-3 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:border-purple-400 dark:focus:border-purple-500 dark:text-gray-300 transition-colors"
|
||||
type="text" placeholder="账号" name="username" required>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input placeholder="密码" type="password" name="password" required
|
||||
class="w-full text-sm px-4 py-3 bg-gray-200 focus:bg-gray-100 border border-gray-200 rounded-lg focus:outline-none focus:border-purple-400">
|
||||
class="w-full text-sm px-4 py-3 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:border-purple-400 dark:focus:border-purple-500 dark:text-gray-300 transition-colors">
|
||||
</div>
|
||||
|
||||
<#if error??>
|
||||
<div class="relative text-center">
|
||||
<span class="text-red-600">${error}</span>
|
||||
<span class="text-red-600 dark:text-red-400">${error}</span>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
<div>
|
||||
<button type="submit"
|
||||
class="w-full flex justify-center bg-purple-800 hover:bg-purple-700 text-gray-100 p-3 rounded-lg tracking-wide font-semibold cursor-pointer transition ease-in duration-500">
|
||||
class="w-full flex justify-center bg-purple-600 hover:bg-purple-700 dark:bg-purple-700 dark:hover:bg-purple-600 text-gray-100 p-3 rounded-lg tracking-wide font-semibold cursor-pointer transition-all duration-300 transform hover:scale-[1.02]">
|
||||
登 录
|
||||
</button>
|
||||
</div>
|
||||
|
@ -1,125 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.bootstrap;
|
||||
|
||||
import com.pig4cloud.pig.auth.support.core.PigDaoAuthenticationProvider;
|
||||
import com.pig4cloud.pig.auth.support.filter.PasswordDecoderFilter;
|
||||
import com.pig4cloud.pig.auth.support.filter.ValidateCodeFilter;
|
||||
import com.pig4cloud.pig.auth.support.handler.PigAuthenticationFailureEventHandler;
|
||||
import com.pig4cloud.pig.auth.support.handler.PigAuthenticationSuccessEventHandler;
|
||||
import com.pig4cloud.pig.auth.support.password.OAuth2ResourceOwnerPasswordAuthenticationProvider;
|
||||
import com.pig4cloud.pig.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationProvider;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* @author lengleng 认证授权服务器配置
|
||||
*/
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class PigBootSecurityServerConfiguration {
|
||||
|
||||
private final AuthenticationConverter accessTokenRequestConverter;
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final PasswordDecoderFilter passwordDecoderFilter;
|
||||
|
||||
private final OAuth2TokenGenerator oAuth2TokenGenerator;
|
||||
|
||||
private final ValidateCodeFilter validateCodeFilter;
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
|
||||
// 配置授权服务器的安全策略,只有/oauth2/**的请求才会走如下的配置
|
||||
http.securityMatcher("/oauth2/**");
|
||||
// 增加验证码过滤器
|
||||
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// 增加密码解密过滤器
|
||||
http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
// 认证服务器配置
|
||||
http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点
|
||||
tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter) // 注入自定义的授权认证Converter
|
||||
.accessTokenResponseHandler(new PigAuthenticationSuccessEventHandler()) // 登录成功处理器
|
||||
.errorResponseHandler(new PigAuthenticationFailureEventHandler());// 登录失败处理器
|
||||
}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证
|
||||
oAuth2ClientAuthenticationConfigurer.errorResponseHandler(new PigAuthenticationFailureEventHandler()))// 处理客户端认证异常
|
||||
, Customizer.withDefaults())
|
||||
.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder().issuer(SecurityConstants.PROJECT_LICENSE).build()),
|
||||
Customizer.withDefaults());
|
||||
|
||||
http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder().issuer(SecurityConstants.PROJECT_LICENSE).build()),
|
||||
Customizer.withDefaults());
|
||||
|
||||
DefaultSecurityFilterChain securityFilterChain = http.build();
|
||||
|
||||
// 注入自定义授权模式实现
|
||||
addCustomOAuth2GrantAuthenticationProvider(http);
|
||||
return securityFilterChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入授权模式实现提供方
|
||||
* <p>
|
||||
* 1. 密码模式 </br>
|
||||
* 2. 短信登录 </br>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) {
|
||||
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
|
||||
OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
|
||||
|
||||
OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator);
|
||||
|
||||
OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator);
|
||||
|
||||
// 处理 UsernamePasswordAuthenticationToken
|
||||
http.authenticationProvider(new PigDaoAuthenticationProvider());
|
||||
// 处理 OAuth2ResourceOwnerPasswordAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);
|
||||
// 处理 OAuth2ResourceOwnerSmsAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerSmsAuthenticationProvider);
|
||||
}
|
||||
|
||||
}
|
@ -17,6 +17,10 @@ spring:
|
||||
enabled: false
|
||||
discovery:
|
||||
enabled: false
|
||||
freemarker: # freemarker 配置,授权码模式页面渲染使用
|
||||
suffix: .ftl
|
||||
template-loader-path: classpath:/templates/
|
||||
request-context-attribute: request
|
||||
main:
|
||||
allow-bean-definition-overriding: true # 允许覆盖bean定义
|
||||
profiles:
|
||||
@ -35,11 +39,10 @@ security:
|
||||
- /swagger-ui.html
|
||||
- /swagger-ui/**
|
||||
- /swagger-resources
|
||||
- /token/check_token
|
||||
- /code/image
|
||||
- /error
|
||||
- /token/**
|
||||
- /actuator/**
|
||||
- /code/**
|
||||
|
||||
#--------------如下配置尽量不要变动-------------
|
||||
# mybatis-plus 配置
|
||||
|
@ -125,6 +125,6 @@ public interface SecurityConstants {
|
||||
/**
|
||||
* 授权码模式confirm
|
||||
*/
|
||||
String CUSTOM_CONSENT_PAGE_URI = "/token/confirm_access";
|
||||
String CUSTOM_CONSENT_PAGE_URI = "/oauth2/confirm_access";
|
||||
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import java.util.Map;
|
||||
public class RepeatBodyRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private final byte[] bodyByteArray;
|
||||
|
||||
private final Map<String, String[]> parameterMap;
|
||||
|
||||
public RepeatBodyRequestWrapper(HttpServletRequest request) {
|
||||
@ -85,7 +86,8 @@ public class RepeatBodyRequestWrapper extends HttpServletRequestWrapper {
|
||||
byte[] body = new byte[0];
|
||||
try {
|
||||
body = StreamUtils.copyToByteArray(request.getInputStream());
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("解析流中数据异常", e);
|
||||
}
|
||||
return body;
|
||||
@ -115,6 +117,5 @@ public class RepeatBodyRequestWrapper extends HttpServletRequestWrapper {
|
||||
public String[] getParameterValues(String name) {
|
||||
return parameterMap.get(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -19,8 +19,6 @@ package com.pig4cloud.pig.common.security.component;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
@ -33,7 +31,7 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2022-06-04
|
||||
*
|
||||
* <p>
|
||||
* 资源服务器认证授权配置
|
||||
*/
|
||||
@Slf4j
|
||||
@ -50,16 +48,21 @@ public class PigResourceServerConfiguration {
|
||||
|
||||
private final OpaqueTokenIntrospector customOpaqueTokenIntrospector;
|
||||
|
||||
/**
|
||||
* 资源服务器安全配置
|
||||
* @param http http
|
||||
* @return {@link SecurityFilterChain }
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
AntPathRequestMatcher[] requestMatchers = permitAllUrl.getUrls()
|
||||
SecurityFilterChain resourceServer(HttpSecurity http) throws Exception {
|
||||
AntPathRequestMatcher[] permitMatchers = permitAllUrl.getUrls()
|
||||
.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toList()
|
||||
.toArray(new AntPathRequestMatcher[] {});
|
||||
|
||||
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers)
|
||||
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(permitMatchers)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
|
Loading…
Reference in New Issue
Block a user