diff --git a/db/pig_config.sql b/db/pig_config.sql index 9c3d6777..4196b5be 100644 --- a/db/pig_config.sql +++ b/db/pig_config.sql @@ -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}/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, ''); diff --git a/pig-auth/pom.xml b/pig-auth/pom.xml index 4f52db79..0bd3fc8c 100755 --- a/pig-auth/pom.xml +++ b/pig-auth/pom.xml @@ -58,7 +58,7 @@ org.springframework.boot spring-boot-starter-security - + org.springframework.boot spring-boot-starter-freemarker diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfiguration.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfiguration.java index ce38ef98..ad49efd0 100755 --- a/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfiguration.java +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfiguration.java @@ -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(); diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/endpoint/PigTokenEndpoint.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/endpoint/PigTokenEndpoint.java index 227ebfa6..204fd547 100644 --- a/pig-auth/src/main/java/com/pig4cloud/pig/auth/endpoint/PigTokenEndpoint.java +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/endpoint/PigTokenEndpoint.java @@ -73,8 +73,8 @@ import java.util.stream.Collectors; */ @Slf4j @RestController +@RequestMapping @RequiredArgsConstructor -@RequestMapping("/token") public class PigTokenEndpoint { private final HttpMessageConverter 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 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 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 tokenList(@RequestBody Map params) { // 根据分页参数获取对应数据 String key = String.format("%s::*", CacheConstants.PROJECT_OAUTH_ACCESS); diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/core/FormIdentityLoginConfigurer.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/core/FormIdentityLoginConfigurer.java index f9206263..1c588b64 100644 --- a/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/core/FormIdentityLoginConfigurer.java +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/core/FormIdentityLoginConfigurer.java @@ -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登出成功处理 diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/filter/ValidateCodeFilter.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/filter/ValidateCodeFilter.java index 5d68aac0..4b7288a3 100644 --- a/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/filter/ValidateCodeFilter.java +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/filter/ValidateCodeFilter.java @@ -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; } diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/handler/FormAuthenticationFailureHandler.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/handler/FormAuthenticationFailureHandler.java index bf6e4254..7eb2fd7c 100644 --- a/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/handler/FormAuthenticationFailureHandler.java +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/handler/FormAuthenticationFailureHandler.java @@ -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); + } } } diff --git a/pig-auth/src/main/resources/templates/ftl/confirm.ftl b/pig-auth/src/main/resources/templates/ftl/confirm.ftl index 881de7e6..e1887428 100644 --- a/pig-auth/src/main/resources/templates/ftl/confirm.ftl +++ b/pig-auth/src/main/resources/templates/ftl/confirm.ftl @@ -1,117 +1,52 @@ <#assign content>
-

应用授权确认

-
-
- <#if principalName=="anonymousUser"> - 未登录 - <#else> - ${principalName} - -
-
- 技术支持 +

应用授权确认

+
+
+
+ <#if principalName=="anonymousUser"> + 未登录用户 + <#else> + + ${principalName} + + +
-
+
-

将获得以下权限:

-
- <#list scopeList as scope> +

将获得以下权限:

+
+ <#list scopeList as scope>
- - - - - - Pig 第三方授权 - - - - -
-
- -
-
- -
- -
-
-
-

应用授权确认

-
-
- <#if principalName=="anonymousUser"> - 未登录 - <#else> - ${principalName} - -
- -
-
- - - - - -
-
-

将获得以下权限:

-
- <#list scopeList as scope> -
- - -
- + +
-
- -
- 授权后表明你已同意 - 服务协议 -
- -
- -
+
- +
-
- - Copyright © 2021-2025 - PIGCLOUD - +
+ 授权后表明你已同意 + 服务协议 +
+ +
+
-
-
+ + - - - - - - +<#include "layout/base.ftl"> diff --git a/pig-auth/src/main/resources/templates/ftl/layout/base.ftl b/pig-auth/src/main/resources/templates/ftl/layout/base.ftl index 386a86e8..0d5fae7d 100644 --- a/pig-auth/src/main/resources/templates/ftl/layout/base.ftl +++ b/pig-auth/src/main/resources/templates/ftl/layout/base.ftl @@ -1,37 +1,75 @@ - + <#if title??>${title}<#else>Pig 统一身份认证</#if> + + <#if extraHead??>${extraHead} - - -
+ + +
+
-
+ +
+
+ +
-
-