├── .gitignore ├── LICENSE ├── pom.xml ├── readme.md ├── settings.xml └── src └── main ├── java └── com │ └── muggle │ └── poseidon │ ├── adapter │ ├── DisableSecurityConfigAdapter.java │ ├── PoseidonAuthConfigAdapter.java │ ├── PoseidonSecurityConfig.java │ ├── SwaggerConfig.java │ └── UserWebConfig.java │ ├── annotation │ └── InterfaceAction.java │ ├── aop │ ├── QueryAspect.java │ └── RequestAspect.java │ ├── auto │ ├── ExpansibilityConfig.java │ ├── PoseidonSecurityProperties.java │ └── SecurityAutoConfig.java │ ├── base │ ├── BaseBean.java │ ├── BaseController.java │ ├── BaseQuery.java │ ├── BaseService.java │ ├── CommonQuery.java │ ├── DistributedLocker.java │ ├── ErrorCode.java │ ├── ResultBean.java │ ├── exception │ │ ├── BasePoseidonCheckException.java │ │ ├── BasePoseidonException.java │ │ ├── SimplePoseidonCheckException.java │ │ └── SimplePoseidonException.java │ └── query │ │ └── CommonQueryBean.java │ ├── entity │ ├── AuthUrlPathDO.java │ └── RequestInfoDO.java │ ├── filter │ ├── SecurityLoginFilter.java │ └── SecurityTokenFilter.java │ ├── handler │ ├── query │ │ └── QuerySqlProcessor.java │ ├── security │ │ ├── PoseidonAccessDeniedHandler.java │ │ ├── PoseidonAuthenticationFailureHandler.java │ │ ├── PoseidonAuthenticationSuccessHandler.java │ │ ├── PoseidonLoginUrlAuthenticationEntryPoint.java │ │ ├── PoseidonLogoutSuccessHandler.java │ │ └── RequestLogProcessor.java │ └── web │ │ ├── RoolMessageHandler.java │ │ ├── WebResultHandler.java │ │ └── WebUrlHandler.java │ ├── listener │ ├── ExceptionEvent.java │ └── ExceptionListener.java │ ├── manager │ ├── PoseidonFilterInvocationSecurityMetadataSource.java │ └── PoseidonWebExpressionVoter.java │ ├── properties │ └── SecurityMessageProperties.java │ ├── service │ └── TokenService.java │ ├── store │ ├── SecurityStore.java │ └── impl │ │ └── SimpleRedisSecurityStore.java │ └── util │ ├── BaseDaoUtils.java │ ├── DateUtils.java │ ├── ICollectionUtils.java │ ├── INumberUtils.java │ ├── IStringUtils.java │ ├── JSONUtils.java │ ├── JwtTokenUtils.java │ ├── LocalDateUtils.java │ ├── NamedThreadFactory.java │ ├── PoseidonIdGenerator.java │ ├── RSACoder.java │ ├── RequestUtils.java │ ├── SHA256Utils.java │ ├── SimpleLocker.java │ ├── ThreadPoolUtils.java │ ├── UserInfoUtils.java │ └── ZipUtils.java └── resources ├── META-INF └── spring.factories ├── banner.txt └── poseidon-logback.xml /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | /mvnw.cmd 33 | /mvnw 34 | /.mvn/ 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.muggle 7 | poseidon-boot-starter 8 | 1.0.0.Beta 9 | poseidon-boot-starter 10 | poseidon 项目starter 包 11 | 12 | 13 | 5.3 14 | 0.7.0 15 | 2.8.0 16 | UTF-8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-configuration-processor 24 | true 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-autoconfigure 32 | 33 | 34 | org.springframework 35 | spring-webmvc 36 | 37 | 38 | jakarta.servlet 39 | jakarta.servlet-api 40 | 6.0.0 41 | 42 | 43 | org.aspectj 44 | aspectjrt 45 | 46 | 47 | 48 | net.logstash.logback 49 | logstash-logback-encoder 50 | ${logstash.version} 51 | 52 | 53 | 54 | org.aspectj 55 | aspectjweaver 56 | 57 | 58 | commons-codec 59 | commons-codec 60 | 1.11 61 | 62 | 63 | io.jsonwebtoken 64 | jjwt 65 | ${jjwt.version} 66 | 67 | 68 | io.springfox 69 | springfox-swagger2 70 | ${swagger2.version} 71 | 72 | 73 | com.github.pagehelper 74 | pagehelper 75 | 5.1.2 76 | 77 | 78 | org.springframework.security 79 | spring-security-web 80 | 81 | 82 | org.springframework.security 83 | spring-security-config 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | org.springframework.boot 92 | spring-boot-dependencies 93 | 3.1.3 94 | pom 95 | import 96 | 97 | 98 | 99 | 100 | 101 | 102 | muggle0-poseidon-real 103 | real 104 | https://muggle0-maven.pkg.coding.net/repository/poseidon/real/ 105 | 106 | true 107 | 108 | 109 | true 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-compiler-plugin 120 | 3.11.0 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![poseidon](https://github.com/muggle0/poseidon-cloud/blob/master/project-document/png/factory.jpg?raw=true) 2 | 3 | 4 | # poseidon-boot-starter 使用说明 5 | 很nice 的 springboot 项目的脚手架,请路过的朋友点一个star。 6 | 7 | 对 springboot 项目感兴趣的小伙伴可以关注 [poseidon](https://github.com/muggle0/poseidon) 8 | 9 | 对 springCloud 感兴趣的小伙伴可以关注 [poseidon-cloud](https://github.com/muggle0/poseidon-cloud) 10 | 11 | 本人的书:[muggle的书](https://muggle-book.gitee.io/) 12 | 13 | 博客:muggle.javaboy.org 14 | 15 | 框架集成功能: 16 | 17 | - 异常报警 18 | - 权限动态配置 19 | - 幂等锁 20 | - 日志分组 21 | - 用户操作日志记录 22 | - 查询接口通用化。 23 | 24 | ## 开发日志 25 | 26 | - 2020.3.9 1.0.0.Beta 发布。多方测试bug 27 | 28 | - 2020.4.5 项目从cloud-starter 分裂出来,补充部分不成熟地方。 29 | 30 | - 2020.4.16 发布 `0.0.1.Beta` 版 31 | 32 | - 2020.4.17 开始开发webflux版,提供对webflux的支持(webflux 分支 版本号0.0.1-webflux.Alpha)。 33 | 34 | - 2020.6.1 `0.0.1.Beta` 添加查询组件功能,并计划发布`0.0.1.release`。 35 | 36 | ## 快速开始 37 | 38 | 具体使用案例可参考 [sofia](https://github.com/fighting-v/sofia) 小伙伴喜欢的可以关注下这个项目嗷。 39 | 40 | 第一步拉取项目 并且使用 maven 安装到本地。 41 | 拉取项目: 42 | ``` 43 | git clone https://github.com/muggle0/poseidon-boot-starter.git 44 | ``` 45 | 安装到本地仓库: 46 | ``` 47 | cd poseidon-boot-starter 48 | 49 | mvn install 50 | 51 | ``` 52 | 53 | 第二步 54 | 创建 spring boot工程 并引入依赖: 55 | 56 | ```xml 57 | 58 | 59 | com.muggle 60 | poseidon-boot-starter 61 | 0.0.1.Beta 62 | 63 | ``` 64 | 第三步开启自动化配置并注册 `tokenService` 和 `securityStore` 65 | 66 | appplication.properties: 67 | 68 | ```properties 69 | spring.profiles.active=dev 70 | # 使用内置logback配置代替spring logback配置 71 | logging.config=classpath:poseidon-logback.xml 72 | # 配置内置logback参数 log文件位置 73 | log.dir=logs 74 | #是否开启自动化配置,开启自动化配置后会注入权限管理,幂等锁,统一异常处理功能 75 | poseidon.auto=true 76 | # 权限管理配置忽略的 url 使用ant匹配符。 77 | poseidon.ignore-path=/** 78 | ``` 79 | 80 | 接下来往spring容器注册 81 | 82 | ```java 83 | 84 | /** 85 | * muggle 86 | */ 87 | @Service 88 | public class SofiaSecurityStore implements SecurityStore { 89 | private static String publicKey="test"; 90 | @Override 91 | public UserDetails getUserdetail(String token) throws BasePoseidonCheckException { 92 | String role = JwtTokenUtils.getBody(token, publicKey, "role"); 93 | String username = JwtTokenUtils.getBody(token, publicKey, "username"); 94 | SofiaUserDO sofiaUserDO = new SofiaUserDO(); 95 | sofiaUserDO.setAccountNonExpired(true); 96 | sofiaUserDO.setAccountNonLocked(true); 97 | SimpleGrantedAuthority admin = new SimpleGrantedAuthority(role); 98 | sofiaUserDO.setAuthorities(Arrays.asList(admin)); 99 | sofiaUserDO.setEnabled(true); 100 | sofiaUserDO.setUsername(username); 101 | return sofiaUserDO; 102 | } 103 | 104 | @Override 105 | public String signUserMessage(UserDetails userDetails) { 106 | Map roleMap=new HashMap<>(); 107 | roleMap.put("username",userDetails.getUsername()); 108 | roleMap.put("role","/admin/**"); 109 | String token = JwtTokenUtils.createToken(roleMap, publicKey, 12L); 110 | return token; 111 | } 112 | @Override 113 | public Boolean cleanToken(String s) { 114 | return null; 115 | } 116 | 117 | } 118 | 119 | 120 | @Component 121 | public class SofiaTokenService implements TokenService { 122 | private AntPathMatcher antPathMatcher=new AntPathMatcher(); 123 | @Override 124 | public UserDetails getUserById(Long aLong) { 125 | return null; 126 | } 127 | 128 | @Override 129 | public boolean rooleMatch(Collection collection, String s) { 130 | boolean match=false; 131 | for (GrantedAuthority grantedAuthority : collection) { 132 | String authority = grantedAuthority.getAuthority(); 133 | match = antPathMatcher.match(authority, s); 134 | } 135 | return match; 136 | } 137 | 138 | 139 | @Override 140 | public void saveUrlInfo(List list) { 141 | 142 | } 143 | 144 | @Override 145 | public UserDetails login(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws SimplePoseidonCheckException { 146 | return loadUserByUsername("admin"); 147 | } 148 | 149 | @Override 150 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 151 | SofiaUserDO sofiaUserDO = new SofiaUserDO(); 152 | sofiaUserDO.setAccountNonExpired(true); 153 | sofiaUserDO.setAccountNonLocked(true); 154 | sofiaUserDO.setEnabled(true); 155 | sofiaUserDO.setUsername(username); 156 | return sofiaUserDO; 157 | } 158 | } 159 | 160 | ``` 161 | 访问自定义的接口,我们就能看到权限拦截成功了。 162 | 163 | ## 接口说明: 164 | 165 | ### 权限相关配置 166 | 167 | `SecurityStore.getUserdetail` 方法 根据token获取用户信息,当请求头 `header` 里面 有 `token` 如: 168 | 169 | ``` 170 | POST http://localhost:8080/test 171 | 172 | Content-Type:application/json 173 | token:eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiYWRtaW4iLCJ1c2VybmFtZSI6Ii9hZG1pbi8qKiJ9.8FOhRpN7DDii2YuuuXdcOU2BofwoEJ6YxCBb4k69sPCGxs9vpH9nd_cTjhvfilvS8itbiUJfgOAT3P9DtDvmiQ 174 | ``` 175 | 这时框架会调用调用该方法来获取用户信息 `userDetails`, 接下来会调用 `userDetails.getAuthorities()` 获取角色集合并获取请求的 uri 一并传递给 `TokenService.rooleMatch` 让实现者自己做权限判断。 176 | 177 | 登录请求的url 为 `/sign_in` 访问该 url 的时候会调用 `TokenService.login` 方法来获取一个token返回给前端。`TokenService.loadUserByUsername` 为 security 框架默认的方法,这里不会去使用它,所以可以只实现该方法,不必去实现其逻辑 178 | 179 | 当 spring 的 `profiles` 也就是配置 `spring.profiles.active` 为 "uat","sit","online","refresh" 时 会获取 swagger 接口注解上的信息并调用: `TokenService.saveUrlInfo()` 的方法,你可以选择直接return 也可以 将这些 url 存到数据库 用于做权限控制 180 | 181 | ### 日志切面相关配置 182 | 183 | 当使用者实现接口 `DistributedLocker` 并注册到spring容器的时候会激活日志切面和幂等拦截。幂等拦截和日志切面使用示例: 184 | 185 | ```java 186 | @RestController 187 | @RequestMapping("/admin") 188 | public class TestController { 189 | 190 | 191 | @GetMapping("/test") 192 | @InterfaceAction(Idempotent = true,expertime = 4L) 193 | public ResultBean test(){ 194 | return ResultBean.success(); 195 | } 196 | 197 | @PostMapping("/test0") 198 | @InterfaceAction 199 | public ResultBean test0(){ 200 | return ResultBean.success(); 201 | } 202 | 203 | } 204 | 205 | ``` 206 | `@InterfaceAction` 是切面注解,当使用该注解的时候 会拦截用户请求 和请求信息打印 info 日志: 207 | 208 | ``` 209 | POSEIDON---- 2020-04-06 12:15:06 [http-nio-8080-exec-3] INFO com.muggle.poseidon.aop.RequestAspect - 请求日志 username=admin url=/admin/test0 method=POST ip=0:0:0:0:0:0:0:1 classMethod=com.fight.controller.TestController.test0 paramters=[] 210 | ``` 211 | 212 | 注解默认不开启幂等拦截,如果想开启幂等拦截需要将 `Idempotent`设置为 true,其他的幂等参数设置 `expertime` 为接口上锁时间, `message` 为幂等拦截后返回给前端的提示信息。 213 | 214 | 可能有部分开发者对用户行为日志写库的需求,我这里未做支持,如果有该需求的开发者可以自己修改 `RequestAspect` 源码。 215 | 216 | 如果你有对日志写库或者二次处理的需求,你只需要实现 `RequestLogProcessor` 接口并注册就能获取到请求与入参。 217 | 218 | ### 框架的基础设施 219 | 220 | 框架收录了平时使用的 utlis 类在 `com.muggle.poseidon.util` 包下,使用者可以按需修改调整。`com.muggle.poseidon.base` 包下提供了基类,基础异常和 `ResultBean` 使用者请根据实际情况按需调整 221 | 222 | ### 统一异常处理相关配置 223 | 224 | `com.muggle.poseidon.handler.web.WebResultHandler.WebResultHandler` 是统一异常处理类,该类定义了部分异常捕获后返回给前端的json信息,用户根据实际情况按需调整。 225 | 226 | 这里需要注意一个 异常报警功能的使用: 227 | 228 | ```java 229 | 230 | @ExceptionHandler(value = {Exception.class}) 231 | public ResultBean exceptionHandler(Exception e, HttpServletRequest req) { 232 | try { 233 | UserDetails userInfo = UserInfoUtils.getUserInfo(); 234 | ExceptionEvent exceptionEvent = new ExceptionEvent(String.format("系统异常: [ %s ] 时间戳: [%d] ", e.getMessage(),System.currentTimeMillis()), this); 235 | applicationContext.publishEvent(exceptionEvent); 236 | log.error("系统异常:" + req.getMethod() + req.getRequestURI()+" user: "+userInfo.toString() , e); 237 | return ResultBean.error("系统异常"); 238 | }catch (Exception err){ 239 | log.error("紧急!!! 严重的异常",err); 240 | return ResultBean.error("系统发生严重的错误"); 241 | } 242 | } 243 | 244 | ``` 245 | 246 | 当系统抛出无法处理的异常的时候,会发布一个事件 `ExceptionEvent` ,我们可以通过监听这个事件来实现系统报警: 247 | 248 | ```java 249 | @Component 250 | public class ExceptionListener implements ApplicationListener { 251 | 252 | 253 | @Override 254 | public void onApplicationEvent(ExceptionEvent event) { 255 | String message = event.getMessage(); 256 | // TODO 将异常信息投递到邮箱等,通知开发人员系统异常,尽快处理。 257 | } 258 | } 259 | ``` 260 | 261 | 262 | 我们还可以添加我们自定义的异常拦截,我们需要,继承 `com.muggle.poseidon.handler.web.WebResultHandler` 然后添加我们需要的处理方法, 示例: 263 | ```java 264 | @RestControllerAdvice 265 | @Configuration 266 | public class MyWebResultHandler extends WebResultHandler { 267 | private static final Logger log = LoggerFactory.getLogger(OAwebResultHandler.class); 268 | @ExceptionHandler({ConstraintViolationException.class}) 269 | public ResultBean methodArgumentNotValidException(ConstraintViolationException e, HttpServletRequest req) { 270 | log.error("参数未通过校验", e); 271 | ResultBean error = ResultBean.error(e.getConstraintViolations().iterator().next().getMessage()); 272 | return error; 273 | } 274 | } 275 | ``` 276 | 示例中添加了一个参数校验异常处理方法。 277 | 278 | 对于其他的异常处理逻辑请阅读源码 `com.muggle.poseidon.handler.web.WebResultHandler` 类。 279 | 280 | ### 查询组件的使用。 281 | 282 | 为了减少开发者对查询接口开发的开发时间,该框架设计了一个简单的配合 `PageHelper` 使用的插件,该功能由于构思不是很成熟, 283 | 所以其部分实现需要框架的使用者自己去完成。下面介绍该功能的使用方式和原理: 284 | 285 | 第一步注册查询拦截切面: 286 | ```java 287 | @Bean 288 | QueryAspect getQueryAspect(){ 289 | return new QueryAspect(); 290 | } 291 | ``` 292 | 293 | 第二步定义查询类: 294 | 295 | ```java 296 | public class MyQuery extends BaseQuery { 297 | 298 | @ApiModelProperty(value = "是否有效") 299 | private Boolean enable; 300 | 301 | @ApiModelProperty(value = "请求类型") 302 | private String requestType; 303 | 304 | @ApiModelProperty(value = "类名") 305 | private String className; 306 | 307 | @ApiModelProperty(value = "方法名") 308 | private String methodName; 309 | 310 | @ApiModelProperty(value = "请求路径") 311 | private String url; 312 | 313 | @ApiModelProperty(value = "描述") 314 | private String description; 315 | 316 | 317 | private String finalSql; 318 | 319 | @Override 320 | public void processSql() { 321 | Map operatorMap = this.getOperatorMap(); 322 | StringBuilder builder=new StringBuilder(); 323 | if (operatorMap!=null){ 324 | Iterator iterator = operatorMap.keySet().iterator(); 325 | while (iterator.hasNext()) { 326 | String next = iterator.next(); 327 | try { 328 | Object field=getFieldValue(next); 329 | if ((field instanceof Number)){ 330 | builder.append(next+operatorMap.get(next).getValue()+field); 331 | }else { 332 | builder.append(next+operatorMap.get(next).getValue()+"'"+field+"'"); 333 | } 334 | } catch (NoSuchFieldException | IllegalAccessException e) { 335 | throw new OAException("查询参数异常:"+next); 336 | } 337 | } 338 | } 339 | 340 | List groupBy = this.getGroupBy(); 341 | if (!CollectionUtils.isEmpty(groupBy)){ 342 | builder.append(" group by"); 343 | for (int i = 0; i < groupBy.size(); i++) { 344 | if (i==groupBy.size()-1){ 345 | builder.append(groupBy.get(i)); 346 | }else { 347 | builder.append(groupBy.get(i)+","); 348 | } 349 | } 350 | } 351 | this.finalSql=builder.toString(); 352 | } 353 | 354 | private Object getFieldValue(String next) throws NoSuchFieldException, IllegalAccessException { 355 | Field field = this.getClass().getDeclaredField(next); 356 | //打开私有访问 357 | field.setAccessible(true); 358 | //获取属性值 359 | return field.get(this); 360 | } 361 | 362 | @Override 363 | public String getFinalSql() { 364 | return finalSql; 365 | } 366 | 367 | public void setFinalSql(String finalSql) { 368 | this.finalSql = finalSql; 369 | } 370 | } 371 | ``` 372 | 373 | 然后在 `mybatis` 的xml中使用 `finalSql` : 374 | ```xml 375 | 382 | ``` 383 | 这个功能的设计思路是 在 `BaseQuery` 的 `orderBy` 字段为排序的list,`groupBy` 是 排序的list, `operatorMap` 是字段的运算符。 384 | 在 `QueryAspect` 的切面中它做的事情很简单,调用 `processSql()` 方法和 `init()` 方法,同时会调用 `QuerySqlProcessor` 的方法进行返回值和查询参数的自定义处理。 385 | 所以这个功能只定义最基础的骨架,其内部的具体实现还是要使用者自己去完成。 386 | 387 | 388 | 389 | ### 日志配置 390 | 391 | 使用框架logback配置:`logging.config=classpath:poseidon-logback.xml` 具体配置信息信息在源码中有注释。 392 | 393 | 394 | 395 | ## 框架使用及其二次开发建议 396 | 397 | 1. 权限控制: 因为不同项目对权限的管理粒度不一样,所以框架将这一部分暴露给使用者实现;关于权限的管控思路——粗粒度权限管控的可以以url的命名来简单管控如 `/admin/**` 的url 只能 admin角色访问,以此类推。 398 | 399 | 对于粒度再细一点的,可以将需要进行权限管控的url 缓存到内存,然后通过用户角色来判断是否有权限访问该url。 400 | 401 | 对于粒度更加细一点的权限控制,可以结合上面两种方法做权限管控,从url命名上约束接口是否需要权限控制,然后再将角色的url权限保存到数据库中。 402 | 403 | 2. 自动化配置扩展: 部分使用者可能希望诸如自定义的序列化器,拦截器,过滤器,监听器等也能根据项目需要实现自动化配置,我们可以在框架中加入我们自己的bean 404 | 405 | 假如我现在想自动化配置一个监听器: 406 | 407 | ```java 408 | public class ExceptionListener implements ApplicationListener { 409 | 410 | 411 | @Override 412 | public void onApplicationEvent(ExceptionEvent event) { 413 | 414 | } 415 | } 416 | ``` 417 | 418 | 我们在 `com.muggle.poseidon.auto.ExpansibilityConfig` 类中注册bean: 419 | 420 | ```java 421 | @Bean 422 | ExceptionListener listener(){ 423 | return new ExceptionListener(); 424 | } 425 | ``` 426 | 427 | 就可以让引用该starter包的spring容器中注册该监听器,或者你也可以在 `spring.factories` 加上你的类限定名: 428 | 429 | ```properties 430 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 431 | com.muggle.poseidon.handler.web.WebResultHandler,\ 432 | com.username.lestener.ExceptionListener 433 | ``` 434 | 435 | 436 | ## 交流 437 | 微信号:muggle_wx 438 | 439 | 喜欢的朋友 starter 一下吧,撸码不容易 440 | 441 | -------------------------------------------------------------------------------- /settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | muggle0-poseidon-real 10 | ${env.CODING_MAVEN_REG_USERNAME} 11 | ${env.CODING_MAVEN_REG_PASSWORD} 12 | 13 | 14 | 15 | 16 | a1 17 | 18 | 19 | central 20 | http://repo1.maven.org/maven2/ 21 | 22 | true 23 | 24 | 25 | false 26 | 27 | 28 | 29 | muggle0-poseidon-real 30 | real 31 | https://muggle0-maven.pkg.coding.net/repository/poseidon/real/ 32 | 33 | true 34 | 35 | 36 | true 37 | 38 | 39 | 40 | 41 | 42 | central 43 | https://maven.aliyun.com/nexus/content/groups/public 44 | 45 | true 46 | 47 | 48 | false 49 | 50 | 51 | 52 | 53 | 54 | 55 | a1 56 | 57 | 58 | 59 | aliyunmaven 60 | central 61 | aliyun maven 62 | https://maven.aliyun.com/repository/public 63 | 64 | 65 | nexus-tencentyun 66 | 67 | !muggle0-poseidon-real 68 | Nexus tencentyun 69 | http://mirrors.cloud.tencent.com/nexus/repository/maven-public/ 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/adapter/DisableSecurityConfigAdapter.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.adapter; 2 | 3 | import com.muggle.poseidon.auto.PoseidonSecurityProperties; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | 10 | /** 11 | * Description 12 | * Date 2021/3/28 13 | * Created by muggle 14 | */ 15 | @Configuration 16 | @EnableConfigurationProperties(PoseidonSecurityProperties.class) 17 | @ConditionalOnProperty(prefix = "poseidon", name = "auto", havingValue = "false", matchIfMissing = false) 18 | public class DisableSecurityConfigAdapter extends WebSecurityConfigurerAdapter { 19 | 20 | @Override 21 | protected void configure(HttpSecurity http) throws Exception { 22 | http 23 | .csrf() 24 | .disable() 25 | .authorizeRequests() 26 | .anyRequest() 27 | .permitAll() 28 | .and() 29 | .logout() 30 | .permitAll(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/adapter/PoseidonAuthConfigAdapter.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.adapter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.google.common.collect.Lists; 7 | import com.muggle.poseidon.auto.PoseidonSecurityProperties; 8 | import com.muggle.poseidon.filter.SecurityLoginFilter; 9 | import com.muggle.poseidon.filter.SecurityTokenFilter; 10 | import com.muggle.poseidon.handler.security.PoseidonAccessDeniedHandler; 11 | import com.muggle.poseidon.handler.security.PoseidonAuthenticationFailureHandler; 12 | import com.muggle.poseidon.handler.security.PoseidonAuthenticationSuccessHandler; 13 | import com.muggle.poseidon.handler.security.PoseidonLoginUrlAuthenticationEntryPoint; 14 | import com.muggle.poseidon.handler.security.PoseidonLogoutSuccessHandler; 15 | import com.muggle.poseidon.manager.PoseidonWebExpressionVoter; 16 | import com.muggle.poseidon.properties.SecurityMessageProperties; 17 | import com.muggle.poseidon.service.TokenService; 18 | import com.muggle.poseidon.store.SecurityStore; 19 | import org.apache.commons.logging.Log; 20 | import org.apache.commons.logging.LogFactory; 21 | import org.springframework.security.access.AccessDecisionManager; 22 | import org.springframework.security.access.AccessDecisionVoter; 23 | import org.springframework.security.access.vote.UnanimousBased; 24 | import org.springframework.security.config.Customizer; 25 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 26 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 27 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 28 | import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; 29 | import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; 30 | import org.springframework.security.config.http.SessionCreationPolicy; 31 | import org.springframework.security.web.AuthenticationEntryPoint; 32 | import org.springframework.security.web.DefaultSecurityFilterChain; 33 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 34 | import org.springframework.security.web.authentication.logout.LogoutFilter; 35 | import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; 36 | 37 | /** 38 | * @program: poseidon-cloud-starter 39 | * @description: 权限校验配置类 40 | * @author: muggle 41 | * @create: 2019-11-04 42 | **/ 43 | @EnableWebSecurity 44 | public class PoseidonAuthConfigAdapter { 45 | private static final Log log = LogFactory.getLog(PoseidonAuthConfigAdapter.class); 46 | private TokenService tokenService; 47 | private SecurityStore securityStore; 48 | private PoseidonSecurityProperties properties; 49 | 50 | public PoseidonAuthConfigAdapter(TokenService tokenService, SecurityStore securityStore, PoseidonSecurityProperties properties) { 51 | this.tokenService = tokenService; 52 | this.securityStore = securityStore; 53 | this.properties = properties; 54 | } 55 | 56 | 57 | 58 | 59 | 60 | public void configure(WebSecurity web) throws Exception { 61 | 62 | // String [] paths={"/**/*.bmp", "/**/*.gif", "/**/*.png", "/**/*.jpg", "/**/*.ico","/**/*.html","/**/*.css","/**/*.js"}; 63 | if (properties.getStaticPath() == null) { 64 | properties.setStaticPath(new ArrayList<>()); 65 | } 66 | String[] paths = new String[properties.getStaticPath().size()]; 67 | properties.getStaticPath().toArray(paths); 68 | web.ignoring().antMatchers(paths); 69 | SecurityStore.ACCESS_PATHS.add("/error_message"); 70 | SecurityStore.ACCESS_PATHS.add("/"); 71 | SecurityStore.ACCESS_PATHS.add("/public/notfound"); 72 | // log.debug("》》》》 初始化security 放行静态资源:{}" + "/**/*.bmp /**/*.png /**/*.gif /**/*.jpg /**/*.ico /**/*.js"); 73 | } 74 | 75 | public DefaultSecurityFilterChain configure(HttpSecurity http) throws Exception { 76 | log.info(">>>>>>>>>>>>>>>>>>>>> 启动security配置 <<<<<<<<<<<<<<<<<<<< "); 77 | 78 | List ignorePath = properties.getIgnorePath(); 79 | 80 | if (ignorePath == null) { 81 | properties.setIgnorePath(new ArrayList<>()); 82 | ignorePath = properties.getIgnorePath(); 83 | } 84 | String[] paths = new String[ignorePath.size()]; 85 | ignorePath.toArray(paths); 86 | SecurityStore.saveAccessPath(ignorePath); 87 | http.authorizeRequests().requestMatchers(paths).permitAll() 88 | .requestMatchers("/admin/oauth/**").hasRole("admin") 89 | .anyRequest().authenticated().accessDecisionManager(accessDecisionManager()) 90 | .and().csrf(new Customizer>() { 91 | @Override 92 | public void customize(CsrfConfigurer httpSecurityCsrfConfigurer) { 93 | 94 | } 95 | }); 96 | http.formLogin(new Customizer>() { 97 | @Override 98 | public void customize(FormLoginConfigurer httpSecurityFormLoginConfigurer) { 99 | 100 | } 101 | }); 102 | http.addFilterBefore(poseidonTokenFilter(), LogoutFilter.class); 103 | http.exceptionHandling().authenticationEntryPoint(loginUrlAuthenticationEntryPoint()).accessDeniedHandler(new PoseidonAccessDeniedHandler()); 104 | http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER); 105 | http.addFilterAt(getLoginFilter(), UsernamePasswordAuthenticationFilter.class); 106 | http.logout().logoutUrl(SecurityMessageProperties.LOGOUT).logoutSuccessHandler(getLogoutSuccessHandler()).permitAll(); 107 | return http.build(); 108 | } 109 | 110 | 111 | /** 112 | * @author muggle 113 | * @Description: 设置投票器 114 | * @Param: 115 | * @return: 116 | * @date 2019/11/6 8:38 117 | */ 118 | private AccessDecisionManager accessDecisionManager() { 119 | List> decisionVoters = Lists.newArrayList(((AccessDecisionVoter) 120 | new PoseidonWebExpressionVoter(tokenService, properties))); 121 | return new UnanimousBased(decisionVoters); 122 | 123 | } 124 | 125 | /** 126 | * @author muggle 127 | * @Description: token的过滤器 128 | * @Param: 129 | * @return: 130 | * @date 2019/11/6 8:38 131 | */ 132 | private SecurityTokenFilter poseidonTokenFilter() { 133 | final SecurityTokenFilter poseidonTokenFilter = new SecurityTokenFilter(securityStore, properties); 134 | return poseidonTokenFilter; 135 | } 136 | 137 | /** 138 | * @author muggle 139 | * @Description: 未登陆处理器,当url为登陆权限时放行 140 | * @Param: 141 | * @return: 142 | * @date 2019/11/5 12:01 143 | */ 144 | private AuthenticationEntryPoint loginUrlAuthenticationEntryPoint() { 145 | return new PoseidonLoginUrlAuthenticationEntryPoint(SecurityMessageProperties.LOGIN_URL); 146 | } 147 | 148 | private SecurityLoginFilter getLoginFilter() { 149 | SecurityLoginFilter securityLoginFilter = new SecurityLoginFilter(tokenService, securityStore); 150 | securityLoginFilter.setAuthenticationFailureHandler(new PoseidonAuthenticationFailureHandler()); 151 | securityLoginFilter.setAuthenticationSuccessHandler(new PoseidonAuthenticationSuccessHandler()); 152 | securityLoginFilter.setFilterProcessesUrl(SecurityMessageProperties.LOGIN_URL); 153 | return securityLoginFilter; 154 | } 155 | 156 | /** 157 | * 登出成功处理器 158 | * 159 | * @return 160 | */ 161 | private LogoutSuccessHandler getLogoutSuccessHandler() { 162 | return new PoseidonLogoutSuccessHandler(this.securityStore); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/adapter/PoseidonSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.adapter; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 7 | import org.springframework.security.config.http.SessionCreationPolicy; 8 | import org.springframework.security.web.SecurityFilterChain; 9 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 10 | 11 | /** 12 | * Description 13 | * Date 2023/10/7 14 | * Created by muggle 15 | */ 16 | @EnableWebSecurity 17 | @Configuration 18 | public class PoseidonSecurityConfig { 19 | 20 | @Bean 21 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 22 | 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/adapter/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | //package com.muggle.poseidon.adapter; 2 | // 3 | //import org.springframework.context.annotation.Bean; 4 | //import org.springframework.context.annotation.Configuration; 5 | //import springfox.documentation.builders.ApiInfoBuilder; 6 | //import springfox.documentation.builders.PathSelectors; 7 | //import springfox.documentation.builders.RequestHandlerSelectors; 8 | //import springfox.documentation.service.Contact; 9 | //import springfox.documentation.spi.DocumentationType; 10 | //import springfox.documentation.spring.web.plugins.Docket; 11 | //import springfox.documentation.swagger2.annotations.EnableSwagger2; 12 | // 13 | ///** 14 | // * @program: poseidon-cloud-user 15 | // * @description: 接口文档配置 16 | // * @author: muggle 17 | // * @create: 2019-12-06 18 | // **/ 19 | // 20 | //@Configuration 21 | //@EnableSwagger2 22 | //public class SwaggerConfig { 23 | // 24 | // @Bean 25 | // public Docket createRestApi() { 26 | // return new Docket(DocumentationType.SWAGGER_2) 27 | // .pathMapping("/") 28 | // .select() 29 | // .apis(RequestHandlerSelectors.basePackage("com.muggle.poseidon.controller")) 30 | // .paths(PathSelectors.any()) 31 | // .build().apiInfo(new ApiInfoBuilder() 32 | // .title("用户中心api") 33 | // .description("用户管理界面") 34 | // .version("0.0.1.BUILD") 35 | // .contact(new Contact("啊啊啊啊","test","isocket@outlook.com")) 36 | // .license("The Apache License") 37 | // .licenseUrl("http://www.baidu.com") 38 | // .build()); 39 | // } 40 | // 41 | //} 42 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/adapter/UserWebConfig.java: -------------------------------------------------------------------------------- 1 | //package com.muggle.poseidon.adapter; 2 | // 3 | // 4 | //import com.alibaba.fastjson.serializer.SerializerFeature; 5 | //import com.alibaba.fastjson.support.config.FastJsonConfig; 6 | //import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; 7 | //import com.muggle.poseidon.base.config.interceptor.RequestLockInterceptor; 8 | //import com.muggle.poseidon.helper.RequestLockHelper; 9 | //import org.springframework.beans.factory.annotation.Autowired; 10 | //import org.springframework.boot.web.server.ErrorPage; 11 | //import org.springframework.boot.web.server.WebServerFactoryCustomizer; 12 | //import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; 13 | //import org.springframework.context.annotation.Bean; 14 | //import org.springframework.context.annotation.Configuration; 15 | //import org.springframework.http.HttpStatus; 16 | //import org.springframework.http.MediaType; 17 | //import org.springframework.http.converter.HttpMessageConverter; 18 | //import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 19 | //import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 20 | // 21 | //import java.util.ArrayList; 22 | //import java.util.List; 23 | // 24 | // 25 | //@Configuration 26 | //public class UserWebConfig implements WebMvcConfigurer { 27 | // 28 | // @Autowired 29 | // List helpers; 30 | // 31 | // 32 | // 33 | // // 解析器 34 | // @Override 35 | // public void configureMessageConverters(List> converters) { 36 | // // 1.需要先定义一个convert 转换消息的对象 37 | // FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); 38 | // // 2.添加fastJson的配置信息,比如,是否需要格式化返回的json数据 39 | // FastJsonConfig fastJsonConfig = new FastJsonConfig(); 40 | // // 空值特别处理 41 | // // WriteNullListAsEmpty 将Collection类型字段的字段空值输出为[] 42 | // // WriteNullStringAsEmpty 将字符串类型字段的空值输出为空字符串 "" 43 | // // WriteNullNumberAsZero 将数值类型字段的空值输出为0 44 | // // WriteNullBooleanAsFalse 将Boolean类型字段的空值输出为false 45 | // fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat, SerializerFeature.WriteNullListAsEmpty, 46 | // SerializerFeature.WriteNullStringAsEmpty); 47 | // // 处理中文乱码问题 48 | // List fastMediaTypes = new ArrayList<>(); 49 | // fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); 50 | // fastConverter.setSupportedMediaTypes(fastMediaTypes); 51 | // // 3.在convert中添加配置信息 52 | // fastConverter.setFastJsonConfig(fastJsonConfig); 53 | // // 4.将convert添加到converters当中 54 | // converters.add(fastConverter); 55 | // } 56 | // 57 | // @Override 58 | // public void addInterceptors(InterceptorRegistry registry) { 59 | // registry.addInterceptor(new RequestLockInterceptor(helpers)).addPathPatterns("/**"); 60 | // 61 | // } 62 | // 63 | // // 配置错误页面 64 | // @Bean 65 | // public WebServerFactoryCustomizer containerCustomizer() { 66 | // return new WebServerFactoryCustomizer() { 67 | // @Override 68 | // public void customize(ConfigurableServletWebServerFactory factory) { 69 | // factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,"/public/test")); 70 | // } 71 | // }; 72 | // } 73 | //} 74 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/annotation/InterfaceAction.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * @description: 开启日志的注解,标注到方法上 11 | * @author: mozishu 12 | * @create: 2019-12-28 12:40 13 | */ 14 | @Documented 15 | @Target(ElementType.METHOD) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | public @interface InterfaceAction { 18 | String message() default "请求太频繁,请稍后再试"; 19 | 20 | /** 21 | * 幂等 22 | * @return 23 | */ 24 | boolean Idempotent() default false; 25 | 26 | long expertime() default 3L; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/aop/QueryAspect.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.aop; 2 | 3 | import com.github.pagehelper.Page; 4 | import com.muggle.poseidon.base.BaseQuery; 5 | import com.muggle.poseidon.base.ResultBean; 6 | import com.muggle.poseidon.handler.query.QuerySqlProcessor; 7 | import org.apache.commons.logging.Log; 8 | import org.apache.commons.logging.LogFactory; 9 | import org.aspectj.lang.JoinPoint; 10 | import org.aspectj.lang.annotation.AfterReturning; 11 | import org.aspectj.lang.annotation.Aspect; 12 | import org.aspectj.lang.annotation.Before; 13 | import org.aspectj.lang.annotation.Pointcut; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | 16 | /** 17 | * @Description: 18 | * @Author: muggle 19 | * @Date: 2020/6/6 20 | **/ 21 | 22 | @Aspect 23 | public class QueryAspect { 24 | 25 | @Autowired(required = false) 26 | QuerySqlProcessor sqlProcessor; 27 | private static final Log log = LogFactory.getLog(QueryAspect.class); 28 | 29 | public QueryAspect() { 30 | log.info(">>>>>>>>>>>>>>>>>>>>>>> 查询切面注册 <<<<<<<<<<<<<<<<<<<<<"); 31 | } 32 | 33 | @Pointcut("execution(* *..*.*Controller.*(com.muggle.poseidon.base.BaseQuery+))") 34 | public void query() { 35 | } 36 | 37 | @Before("query()") 38 | public void doBefore(JoinPoint joinPoint) { 39 | Object[] args = joinPoint.getArgs(); 40 | if (args.length < 1) { 41 | return; 42 | } 43 | for (Object arg : args) { 44 | if (arg instanceof BaseQuery) { 45 | BaseQuery query = (BaseQuery) arg; 46 | query.init(); 47 | if (sqlProcessor != null) { 48 | sqlProcessor.beforeQuery(query); 49 | } 50 | query.processSql(); 51 | } 52 | } 53 | } 54 | 55 | @AfterReturning(pointcut = "query()", returning = "result") 56 | public void doAfterReturning(JoinPoint joinPoint, Object result) { 57 | if (result == null) { 58 | return; 59 | } 60 | if (result instanceof ResultBean) { 61 | Object data = ((ResultBean) result).getData(); 62 | if (sqlProcessor != null) { 63 | sqlProcessor.afterReturningQuery((ResultBean) result); 64 | } 65 | if (data instanceof Page) { 66 | ((ResultBean) result).setTotal(((Page) data).getTotal()); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/aop/RequestAspect.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.aop; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | 5 | import com.muggle.poseidon.annotation.InterfaceAction; 6 | import com.muggle.poseidon.base.DistributedLocker; 7 | import com.muggle.poseidon.base.exception.BasePoseidonCheckException; 8 | import com.muggle.poseidon.base.exception.SimplePoseidonException; 9 | import com.muggle.poseidon.handler.security.RequestLogProcessor; 10 | import com.muggle.poseidon.util.RequestUtils; 11 | import com.muggle.poseidon.util.UserInfoUtils; 12 | import org.apache.commons.logging.Log; 13 | import org.apache.commons.logging.LogFactory; 14 | import org.aspectj.lang.JoinPoint; 15 | import org.aspectj.lang.annotation.AfterReturning; 16 | import org.aspectj.lang.annotation.Aspect; 17 | import org.aspectj.lang.annotation.Before; 18 | import org.aspectj.lang.annotation.Pointcut; 19 | import org.aspectj.lang.reflect.MethodSignature; 20 | import org.springframework.security.core.Authentication; 21 | import org.springframework.security.core.context.SecurityContextHolder; 22 | import org.springframework.security.core.userdetails.UserDetails; 23 | import org.springframework.web.context.request.RequestContextHolder; 24 | import org.springframework.web.context.request.ServletRequestAttributes; 25 | 26 | 27 | /** 28 | * @description: 日志aop类 29 | * @author: muggle 30 | * @create: 2019-12-26 16:40 31 | */ 32 | @Aspect 33 | public class RequestAspect { 34 | private DistributedLocker locker; 35 | 36 | private RequestLogProcessor logProcessor; 37 | 38 | public RequestAspect(DistributedLocker locker, RequestLogProcessor logProcessor) { 39 | this.logProcessor = logProcessor; 40 | this.locker = locker; 41 | log.info(">>>>>>>>>>>>>>>>>>>>>>>[请求日志切面初始化]<<<<<<<<<<<<<<<<<<<<<"); 42 | } 43 | 44 | private static final Log log = LogFactory.getLog(RequestAspect.class); 45 | 46 | @Pointcut("@annotation(com.muggle.poseidon.annotation.InterfaceAction)") 47 | public void request() { 48 | } 49 | 50 | @Before("request()") 51 | public void doBefore(JoinPoint joinPoint) { 52 | log.debug(">>>>>>>>>>>>>>>>>>>>>>> 请求日志切面 dobefore <<<<<<<<<<<<<<<<<<<<<"); 53 | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 54 | HttpServletRequest request = attributes.getRequest(); 55 | this.verifyIdempotent(joinPoint); 56 | //参数 57 | Object[] args = joinPoint.getArgs(); 58 | StringBuilder stringBuilder = new StringBuilder(); 59 | for (Object arg : args) { 60 | stringBuilder.append(" (").append(arg.toString()).append(") "); 61 | } 62 | 63 | String userMessage = "用户名:%s"; 64 | try { 65 | UserDetails userInfo = UserInfoUtils.getUserInfo(); 66 | userMessage = String.format(userMessage, userInfo == null ? "用户未登录" : userInfo.getUsername()); 67 | } catch (BasePoseidonCheckException e) { 68 | userMessage = String.format(userMessage, "非法的登录用户"); 69 | } 70 | log.info("》》》》》》 请求日志 " + userMessage + " url=" + request.getRequestURI() + "method=" + request.getMethod() + "ip=" + request.getRemoteAddr() 71 | + "host=" + request.getRemoteHost() + "port=" + request.getRemotePort() 72 | + "classMethod=" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() 73 | + "paramters [ " + stringBuilder.toString() + " ]"); 74 | if (logProcessor != null) { 75 | logProcessor.recordBefore(request, joinPoint.getArgs()); 76 | } 77 | } 78 | 79 | /** 80 | * 幂等操作 @param joinPoint 81 | */ 82 | private void verifyIdempotent(JoinPoint joinPoint) { 83 | InterfaceAction annotation = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(InterfaceAction.class); 84 | boolean idempotent = annotation.Idempotent(); 85 | if (idempotent) { 86 | log.debug(">>>>>>>>>>>>>>>>>>>>>>> 幂等操作 <<<<<<<<<<<<<<<<<<<<<"); 87 | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 88 | HttpServletRequest request = attributes.getRequest(); 89 | String requestURI = request.getRequestURI(); 90 | String remoteAddr = RequestUtils.getIP(request); 91 | log.info(String.format("请求进入幂等方法,开始尝试上锁 uri: %s ip:%s", requestURI, remoteAddr)); 92 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 93 | String username = "notSignin"; 94 | if (authentication != null && authentication.getPrincipal() == null) { 95 | username = authentication.getPrincipal().toString(); 96 | } 97 | String key = "lock:idempotent:" + request.getRequestURI() + ":" + username + ":" + RequestUtils.getIP(request); 98 | long expertime = annotation.expertime(); 99 | log.info("冪等上锁 key :" + key); 100 | boolean trylock = false; 101 | try { 102 | trylock = locker.tryLock(key, expertime); 103 | } catch (InterruptedException e) { 104 | log.error(e); 105 | } 106 | if (!trylock) { 107 | String message = annotation.message(); 108 | log.info(">>>>>>>>>>>>>>>>>>>>>> 获取幂等锁获取锁失败 key:" + key + " <<<<<<<<<<<<<<<<<<<<<<<"); 109 | throw new SimplePoseidonException(message); 110 | } 111 | } 112 | } 113 | 114 | 115 | @AfterReturning(pointcut = "request()", returning = "result") 116 | public void doAfterReturning(JoinPoint joinPoint, Object result) { 117 | log.debug(">>>>>>>>>>>>>>>>>>>>>> 操作日志,返回值切面执行 <<<<<<<<<<<<<<<<<<<<<<< "); 118 | if (logProcessor != null) { 119 | logProcessor.recordAfterReturning(result, joinPoint.getArgs()); 120 | } 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/auto/ExpansibilityConfig.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.auto; 2 | 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * 二次开发的扩展配置类 11 | * muggle 12 | */ 13 | 14 | @ConditionalOnProperty(prefix = "poseidon", name = "auto", havingValue = "true", matchIfMissing = false) 15 | @Configuration 16 | //@ConditionalOnProperty(TokenService.class) 17 | public class ExpansibilityConfig { 18 | 19 | /** 20 | * logger 21 | */ 22 | private static final Logger log = LoggerFactory.getLogger(ExpansibilityConfig.class); 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/auto/PoseidonSecurityProperties.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.auto; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | 7 | /** 8 | * @program: poseidon-cloud-starter 9 | * @description: 10 | * @author: muggle 11 | * @create: 2019-11-04 12 | **/ 13 | 14 | @ConfigurationProperties(prefix = "poseidon") 15 | public class PoseidonSecurityProperties { 16 | 17 | /** 18 | * 放行的url 19 | **/ 20 | private List ignorePath; 21 | 22 | private List staticPath; 23 | 24 | public List getStaticPath() { 25 | return staticPath; 26 | } 27 | 28 | public void setStaticPath(List staticPath) { 29 | this.staticPath = staticPath; 30 | } 31 | 32 | public List getIgnorePath() { 33 | 34 | return ignorePath; 35 | } 36 | 37 | public void setIgnorePath(List ignorePath) { 38 | this.ignorePath = ignorePath; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/auto/SecurityAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.auto; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import com.muggle.poseidon.adapter.PoseidonAuthConfigAdapter; 9 | import com.muggle.poseidon.aop.RequestAspect; 10 | import com.muggle.poseidon.base.DistributedLocker; 11 | import com.muggle.poseidon.base.exception.SimplePoseidonException; 12 | import com.muggle.poseidon.entity.AuthUrlPathDO; 13 | import com.muggle.poseidon.handler.security.RequestLogProcessor; 14 | import com.muggle.poseidon.service.TokenService; 15 | import com.muggle.poseidon.store.SecurityStore; 16 | import io.swagger.annotations.Api; 17 | import io.swagger.annotations.ApiOperation; 18 | import org.apache.commons.logging.Log; 19 | import org.apache.commons.logging.LogFactory; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.boot.CommandLineRunner; 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 24 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 25 | import org.springframework.boot.web.server.ErrorPage; 26 | import org.springframework.boot.web.server.WebServerFactoryCustomizer; 27 | import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; 28 | import org.springframework.context.ApplicationContext; 29 | import org.springframework.context.annotation.Bean; 30 | import org.springframework.context.annotation.Configuration; 31 | import org.springframework.context.annotation.Profile; 32 | import org.springframework.http.HttpStatus; 33 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 34 | import org.springframework.security.web.SecurityFilterChain; 35 | import org.springframework.web.bind.annotation.RequestMapping; 36 | import org.springframework.web.bind.annotation.RequestMethod; 37 | import org.springframework.web.context.WebApplicationContext; 38 | import org.springframework.web.method.HandlerMethod; 39 | import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; 40 | import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition; 41 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo; 42 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 43 | 44 | /** 45 | * @program: poseidon-cloud-starter 46 | * @description: 自动化配置核心类 47 | * @author: muggle 48 | * @create: 2019-11-04 49 | **/ 50 | 51 | @Configuration 52 | @EnableConfigurationProperties(PoseidonSecurityProperties.class) 53 | @ConditionalOnProperty(prefix = "poseidon", name = "auto", havingValue = "true", matchIfMissing = false) 54 | //@ConditionalOnProperty(TokenService.class) 55 | @SuppressWarnings("ALL") 56 | public class SecurityAutoConfig { 57 | private static final Log log = LogFactory.getLog(SecurityAutoConfig.class); 58 | 59 | @Autowired 60 | PoseidonSecurityProperties properties; 61 | 62 | @Autowired 63 | private WebApplicationContext applicationContext; 64 | 65 | @Autowired(required = false) 66 | RequestLogProcessor logProcessor; 67 | 68 | @Bean 69 | public PoseidonAuthConfigAdapter getAdapter(TokenService tokenService, SecurityStore securityStore) { 70 | log.info(">>>>>>>>>>>>>>>>>>>>>>> 开启自动化配置 <<<<<<<<<<<<<<<<<<<<<"); 71 | if (securityStore == null) { 72 | throw new SimplePoseidonException("请先注册 securityStore"); 73 | } 74 | return new PoseidonAuthConfigAdapter(tokenService, securityStore, properties); 75 | } 76 | 77 | /** 78 | * 测试和生产环境生效 79 | * 80 | * @param tokenService 81 | * @return 82 | */ 83 | @Bean 84 | @Profile({"uat", "sit", "online", "refresh"}) 85 | public CommandLineRunner init(final TokenService tokenService, ApplicationContext context) { 86 | return new CommandLineRunner() { 87 | @Override 88 | public void run(String... strings) throws Exception { 89 | String property = context.getEnvironment().getProperty("log.dir"); 90 | if (property==null){ 91 | throw new SimplePoseidonException(">>>>>>>>>>>>>>>>>>>>>>> application-log.dir 未配置 <<<<<<<<<<<<<<<<<<<<<"); 92 | } 93 | log.info(">>>>>>>>>>>>>>>>>>>>>>> 权限系统开机任务执行 <<<<<<<<<<<<<<<<<<<<<"); 94 | List allURL = getAllURL(); 95 | tokenService.processUrl(allURL); 96 | } 97 | }; 98 | } 99 | 100 | /** 101 | * 读取所有的url 并交给tokenService.saveUrlInfo处理 102 | **/ 103 | public List getAllURL() { 104 | List resultList = new ArrayList<>(); 105 | 106 | RequestMappingHandlerMapping requestMappingHandlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class); 107 | // 获取url与类和方法的对应信息 108 | Map map = requestMappingHandlerMapping.getHandlerMethods(); 109 | 110 | for (Map.Entry mappingInfoHandlerMethodEntry : map.entrySet()) { 111 | 112 | RequestMappingInfo requestMappingInfo = mappingInfoHandlerMethodEntry.getKey(); 113 | HandlerMethod handlerMethod = mappingInfoHandlerMethodEntry.getValue(); 114 | AuthUrlPathDO authUrlPathDO = new AuthUrlPathDO(); 115 | // 类名 116 | authUrlPathDO.setClassName(handlerMethod.getMethod().getDeclaringClass().getName()); 117 | 118 | Annotation[] parentAnnotations = handlerMethod.getBeanType().getAnnotations(); 119 | for (Annotation annotation : parentAnnotations) { 120 | if (annotation instanceof Api) { 121 | Api api = (Api) annotation; 122 | authUrlPathDO.setClassDesc(api.value()); 123 | } else if (annotation instanceof RequestMapping) { 124 | RequestMapping requestMapping = (RequestMapping) annotation; 125 | if (null != requestMapping.value() && requestMapping.value().length > 0) { 126 | //类URL 127 | authUrlPathDO.setClassUrl(requestMapping.value()[0]); 128 | } 129 | } 130 | } 131 | // 方法名 132 | authUrlPathDO.setMethodName(handlerMethod.getMethod().getName()); 133 | Annotation[] annotations = handlerMethod.getMethod().getDeclaredAnnotations(); 134 | if (annotations != null) { 135 | // 处理具体的方法信息 136 | for (Annotation annotation : annotations) { 137 | if (annotation instanceof ApiOperation) { 138 | ApiOperation methodDesc = (ApiOperation) annotation; 139 | String desc = methodDesc.value(); 140 | //接口描述 141 | authUrlPathDO.setMethodDesc(desc); 142 | } 143 | } 144 | } 145 | PatternsRequestCondition p = requestMappingInfo.getPatternsCondition(); 146 | for (String url : p.getPatterns()) { 147 | //请求URL 148 | authUrlPathDO.setMethodURL(url); 149 | } 150 | RequestMethodsRequestCondition methodsCondition = requestMappingInfo.getMethodsCondition(); 151 | for (RequestMethod requestMethod : methodsCondition.getMethods()) { 152 | //请求方式:POST/PUT/GET/DELETE 153 | authUrlPathDO.setRequestType(requestMethod.toString()); 154 | } 155 | resultList.add(authUrlPathDO); 156 | } 157 | return resultList; 158 | } 159 | 160 | @Bean 161 | @Autowired 162 | @ConditionalOnBean(DistributedLocker.class) 163 | public RequestAspect getLogAspect(DistributedLocker distributedLocker) { 164 | log.info(">>>>>>>>>>>>>>>>>>>>>>> 日志切面注册 <<<<<<<<<<<<<<<<<<<<<"); 165 | return new RequestAspect(distributedLocker, logProcessor); 166 | } 167 | 168 | 169 | // 配置错误页面 170 | @Bean 171 | public WebServerFactoryCustomizer containerCustomizer() { 172 | return new WebServerFactoryCustomizer() { 173 | @Override 174 | public void customize(ConfigurableServletWebServerFactory factory) { 175 | log.info(">>>>>>>>>>>>>>>>>>>>>>> 错误页面配置——404(/public/notfound) 500(/error_message) <<<<<<<<<<<<<<<<<<<<<"); 176 | factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/public/notfound"), new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error_message")); 177 | } 178 | }; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/BaseBean.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base; 2 | 3 | /** 4 | * @program: poseidon-cloud-starter 5 | * @description: base pojo 6 | * @author: muggle 7 | * @create: 2019-11-05 8 | **/ 9 | 10 | public abstract class BaseBean { 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/BaseController.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base; 2 | 3 | /** 4 | * @Description: 5 | * @Author: muggle 6 | * @Date: 2020/11/11 7 | **/ 8 | public abstract class BaseController { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/BaseQuery.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import com.github.pagehelper.PageHelper; 7 | import org.springframework.util.CollectionUtils; 8 | 9 | /** 10 | * @Description: 11 | * @Author: muggle 12 | * @Date: 2020/6/5 13 | **/ 14 | 15 | public abstract class BaseQuery { 16 | 17 | private List orderBy; 18 | 19 | private List groupBy; 20 | 21 | private Map operatorMap; 22 | 23 | private Sort sort = Sort.DESC; 24 | 25 | private int startPage = 1; 26 | 27 | private int pageSize = 10; 28 | 29 | 30 | public void init() { 31 | PageHelper.startPage(startPage, pageSize); 32 | if (!CollectionUtils.isEmpty(orderBy)) { 33 | String join = String.join(",", orderBy); 34 | PageHelper.orderBy(join + " " + sort); 35 | } 36 | } 37 | 38 | /** 39 | * sql 转化 40 | * 41 | * @param 42 | */ 43 | public abstract void processSql(); 44 | 45 | /** 46 | * 在mybati中使用sql注入 形如 where ${finalSql} 47 | * 48 | * @return 49 | */ 50 | public abstract String getFinalSql(); 51 | 52 | 53 | public enum Sort { 54 | DESC, ASC 55 | } 56 | 57 | /** 58 | * 运算符 59 | */ 60 | public enum Operator { 61 | equals("=%s "), 62 | notEqual("<>%s "), 63 | moreThan(">%s "), 64 | lessThan("<%s "), 65 | leftLike("LIKE '%s%%' "), 66 | notNull("%s IS NOT NULL "), 67 | isNull("%s IS NULL "), 68 | in("in (%s) "), 69 | allLike("LIKE '%%%s%%' "); 70 | 71 | private String value; 72 | 73 | Operator(String value) { 74 | this.value = value; 75 | } 76 | 77 | public String getValue() { 78 | return value; 79 | } 80 | 81 | public void setValue(String value) { 82 | this.value = value; 83 | } 84 | } 85 | 86 | 87 | public List getOrderBy() { 88 | return orderBy; 89 | } 90 | 91 | public void setOrderBy(List orderBy) { 92 | this.orderBy = orderBy; 93 | } 94 | 95 | public List getGroupBy() { 96 | return groupBy; 97 | } 98 | 99 | public void setGroupBy(List groupBy) { 100 | this.groupBy = groupBy; 101 | } 102 | 103 | public Map getOperatorMap() { 104 | return operatorMap; 105 | } 106 | 107 | public void setOperatorMap(Map operatorMap) { 108 | this.operatorMap = operatorMap; 109 | } 110 | 111 | public Sort getSort() { 112 | return sort; 113 | } 114 | 115 | public void setSort(Sort sort) { 116 | this.sort = sort; 117 | } 118 | 119 | public int getStartPage() { 120 | return startPage; 121 | } 122 | 123 | public void setStartPage(int startPage) { 124 | this.startPage = startPage; 125 | } 126 | 127 | public int getPageSize() { 128 | return pageSize; 129 | } 130 | 131 | public void setPageSize(int pageSize) { 132 | this.pageSize = pageSize; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/BaseService.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base; 2 | 3 | /** 4 | * @program: poseidon-cloud-starter 5 | * @description: 6 | * @author: muggle 7 | * @create: 2019-11-05 8 | **/ 9 | 10 | public abstract class BaseService { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/CommonQuery.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import com.muggle.poseidon.base.exception.SimplePoseidonException; 9 | import org.springframework.util.CollectionUtils; 10 | 11 | /** 12 | * @Description: 13 | * @Author: muggle 14 | * @Date: 2020/6/6 15 | **/ 16 | public class CommonQuery extends BaseQuery { 17 | 18 | private String finalSql; 19 | 20 | @Override 21 | public void processSql() { 22 | Map operatorMap = this.getOperatorMap(); 23 | StringBuilder builder = new StringBuilder(); 24 | if (operatorMap != null) { 25 | Iterator iterator = operatorMap.keySet().iterator(); 26 | while (iterator.hasNext()) { 27 | String next = iterator.next(); 28 | try { 29 | Object field = getFieldValue(next); 30 | Operator operator = operatorMap.get(next); 31 | builder.append("AND "); 32 | if ((field instanceof Number || Operator.leftLike.equals(operator) || Operator.allLike.equals(operator))) { 33 | builder.append(String.format(next + " " + operator.getValue(), field)); 34 | } else { 35 | builder.append(String.format(next + " " + operator.getValue(), "'" + field + "'")); 36 | } 37 | } catch (NoSuchFieldException | IllegalAccessException e) { 38 | throw new SimplePoseidonException("查询参数异常:" + next); 39 | } 40 | } 41 | } 42 | 43 | List groupBy = this.getGroupBy(); 44 | if (!CollectionUtils.isEmpty(groupBy)) { 45 | builder.append(" group by"); 46 | for (int i = 0; i < groupBy.size(); i++) { 47 | if (i == groupBy.size() - 1) { 48 | builder.append(groupBy.get(i)); 49 | } else { 50 | builder.append(groupBy.get(i) + ","); 51 | } 52 | } 53 | } 54 | this.finalSql = builder.toString(); 55 | } 56 | 57 | private Object getFieldValue(String next) throws NoSuchFieldException, IllegalAccessException { 58 | Field field = this.getClass().getDeclaredField(next); 59 | //打开私有访问 60 | field.setAccessible(true); 61 | //获取属性值 62 | return field.get(this); 63 | } 64 | 65 | @Override 66 | public String getFinalSql() { 67 | return finalSql; 68 | } 69 | 70 | public void setFinalSql(String finalSql) { 71 | this.finalSql = finalSql; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/DistributedLocker.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base; 2 | 3 | import java.util.concurrent.locks.Lock; 4 | 5 | /** 6 | * @program: poseidon-cloud-starter 7 | * @description: 8 | * @author: muggle 9 | * @create: 2019-12-31 10 | **/ 11 | 12 | public interface DistributedLocker extends Lock { 13 | 14 | /** 15 | * 锁 16 | * @param key 17 | * @param expertime 18 | * @return 19 | */ 20 | boolean tryLock(String key, long expertime) throws InterruptedException; 21 | 22 | /** 23 | * 阻塞 24 | */ 25 | void lock(String key); 26 | 27 | /** 28 | * 解锁 29 | */ 30 | void unlock(String key); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/ErrorCode.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base; 2 | 3 | public class ErrorCode { 4 | public static final ErrorCode JSON_ERROR =new ErrorCode("json序列化异常",1001); 5 | private String message; 6 | private int code; 7 | 8 | ErrorCode(String message, int code) { 9 | this.message = message; 10 | this.code = code; 11 | } 12 | 13 | public String getMessage() { 14 | return message; 15 | } 16 | 17 | public void setMessage(String message) { 18 | this.message = message; 19 | } 20 | 21 | public int getCode() { 22 | return code; 23 | } 24 | 25 | public void setCode(int code) { 26 | this.code = code; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/ResultBean.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base; 2 | 3 | 4 | import java.io.Serializable; 5 | 6 | 7 | /** 8 | * @program: poseidon-cloud-starter 9 | * @description: 10 | * @author: muggle 11 | * @create: 2019-11-05 12 | **/ 13 | 14 | public class ResultBean implements Serializable { 15 | 16 | private static final long serialVersionUID = 1553485005784190508L; 17 | private String message; 18 | private Integer code; 19 | private T data; 20 | private Long total; 21 | 22 | public String getMessage() { 23 | return message; 24 | } 25 | 26 | public void setMessage(String message) { 27 | this.message = message; 28 | } 29 | 30 | public Integer getCode() { 31 | return code; 32 | } 33 | 34 | public void setCode(Integer code) { 35 | this.code = code; 36 | } 37 | 38 | public T getData() { 39 | return data; 40 | } 41 | 42 | public void setData(T data) { 43 | this.data = data; 44 | } 45 | 46 | private ResultBean(String message, Integer code, T data) { 47 | this.code = code; 48 | this.message = message; 49 | this.data = data; 50 | } 51 | 52 | private ResultBean(String message, Integer code) { 53 | this.code = code; 54 | this.message = message; 55 | } 56 | 57 | 58 | public static ResultBean getInstance(String message, Integer code, T data) { 59 | return new ResultBean<>(message, code, data); 60 | } 61 | 62 | public static ResultBean getInstance(String message, Integer code) { 63 | return new ResultBean<>(message, code); 64 | } 65 | 66 | public static ResultBean success() { 67 | return new ResultBean<>("请求成功", 200); 68 | } 69 | 70 | public static ResultBean success(String message) { 71 | return new ResultBean<>(message, 200); 72 | } 73 | 74 | public static ResultBean error(String message) { 75 | return new ResultBean<>(message, 5001); 76 | } 77 | 78 | public static ResultBean error(String message, Integer code) { 79 | return new ResultBean<>(message, code); 80 | } 81 | 82 | public static ResultBean successData(T data) { 83 | return new ResultBean<>("请求成功", 200, data); 84 | } 85 | 86 | 87 | public ResultBean() { 88 | 89 | } 90 | 91 | public Long getTotal() { 92 | return total; 93 | } 94 | 95 | public void setTotal(Long total) { 96 | this.total = total; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/exception/BasePoseidonCheckException.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base.exception; 2 | 3 | 4 | /** 5 | * @program: poseidon-cloud-starter 6 | * @description: 7 | * @author: muggle 8 | * @create: 2019-11-06 9 | **/ 10 | 11 | public abstract class BasePoseidonCheckException extends Exception { 12 | 13 | public BasePoseidonCheckException(String message) { 14 | super(message); 15 | } 16 | 17 | public abstract Integer getCode(); 18 | 19 | public BasePoseidonCheckException(Throwable e){ 20 | super(e); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/exception/BasePoseidonException.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base.exception; 2 | 3 | /** 4 | * @program: poseidon-cloud-starter 5 | * @description: 业务异常 6 | * @author: muggle 7 | * @create: 2019-11-05 8 | **/ 9 | 10 | public abstract class BasePoseidonException extends RuntimeException { 11 | 12 | public BasePoseidonException(String message) { 13 | super(message); 14 | } 15 | public BasePoseidonException(Throwable e){ 16 | super(e); 17 | } 18 | 19 | public abstract Integer getCode(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/exception/SimplePoseidonCheckException.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base.exception; 2 | 3 | /** 4 | * @program: poseidon-cloud-starter 5 | * @description: 6 | * @author: muggle 7 | * @create: 2019-11-06 8 | **/ 9 | 10 | public class SimplePoseidonCheckException extends BasePoseidonCheckException { 11 | private Integer code; 12 | 13 | public SimplePoseidonCheckException(String message) { 14 | super(message); 15 | this.code = 5001; 16 | } 17 | 18 | public SimplePoseidonCheckException(String message, Integer code) { 19 | super(message); 20 | this.code = code; 21 | } 22 | 23 | public SimplePoseidonCheckException(Throwable e) { 24 | super(e); 25 | this.code=5001; 26 | } 27 | 28 | @Override 29 | public Integer getCode() { 30 | return code; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/exception/SimplePoseidonException.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base.exception; 2 | 3 | import com.muggle.poseidon.base.ErrorCode; 4 | 5 | /** 6 | * @program: poseidon-cloud-starter 7 | * @description: 通用业务异常 8 | * @author: muggle 9 | * @create: 2019-11-05 10 | **/ 11 | 12 | public class SimplePoseidonException extends BasePoseidonException { 13 | 14 | private Integer code; 15 | 16 | public SimplePoseidonException(String message, Integer code) { 17 | super(message); 18 | this.code = code; 19 | } 20 | 21 | public SimplePoseidonException(String message) { 22 | super(message); 23 | this.code = 5001; 24 | } 25 | 26 | public SimplePoseidonException(Throwable e){ 27 | super(e); 28 | this.code=5001; 29 | } 30 | 31 | public SimplePoseidonException(ErrorCode errorCode) { 32 | super(errorCode.getMessage()); 33 | this.code=errorCode.getCode(); 34 | } 35 | 36 | @Override 37 | public Integer getCode() { 38 | return code; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/base/query/CommonQueryBean.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.base.query; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @Description: 7 | * @Author: muggle 8 | * @Date: 2020/5/20 9 | **/ 10 | public class CommonQueryBean { 11 | private T data; 12 | 13 | private Map operator; 14 | 15 | private String orderBy ; 16 | 17 | private int start; 18 | 19 | private int pageSize; 20 | 21 | private String groupBy; 22 | 23 | public String getSql(){ 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/entity/AuthUrlPathDO.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.entity; 2 | 3 | /** 4 | * @program: poseidon-cloud-starter 5 | * @description: 用于保存所有的url 6 | * @author: muggle 7 | * @create: 2019-11-05 8 | **/ 9 | 10 | public class AuthUrlPathDO { 11 | 12 | private String methodURL; 13 | 14 | private String methodDesc; 15 | 16 | private String requestType; 17 | 18 | private String application; 19 | 20 | private String className; 21 | 22 | private String classDesc; 23 | 24 | private String classUrl; 25 | 26 | private String methodName; 27 | 28 | public String getMethodURL() { 29 | return methodURL; 30 | } 31 | 32 | public void setMethodURL(String methodURL) { 33 | this.methodURL = methodURL; 34 | } 35 | 36 | public String getMethodDesc() { 37 | return methodDesc; 38 | } 39 | 40 | public void setMethodDesc(String methodDesc) { 41 | this.methodDesc = methodDesc; 42 | } 43 | 44 | public String getRequestType() { 45 | return requestType; 46 | } 47 | 48 | public void setRequestType(String requestType) { 49 | this.requestType = requestType; 50 | } 51 | 52 | public String getApplication() { 53 | return application; 54 | } 55 | 56 | public void setApplication(String application) { 57 | this.application = application; 58 | } 59 | 60 | public String getClassName() { 61 | return className; 62 | } 63 | 64 | public void setClassName(String className) { 65 | this.className = className; 66 | } 67 | 68 | public String getClassDesc() { 69 | return classDesc; 70 | } 71 | 72 | public void setClassDesc(String classDesc) { 73 | this.classDesc = classDesc; 74 | } 75 | 76 | public String getClassUrl() { 77 | return classUrl; 78 | } 79 | 80 | public void setClassUrl(String classUrl) { 81 | this.classUrl = classUrl; 82 | } 83 | 84 | public String getMethodName() { 85 | return methodName; 86 | } 87 | 88 | public void setMethodName(String methodName) { 89 | this.methodName = methodName; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/entity/RequestInfoDO.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.entity; 2 | 3 | /** 4 | * @Description: 5 | * @Author: muggle 6 | * @Date: 2020/4/5$ 7 | **/ 8 | public class RequestInfoDO { 9 | private String username; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/filter/SecurityLoginFilter.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.filter; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import jakarta.servlet.http.HttpServletResponse; 5 | 6 | import com.muggle.poseidon.base.exception.SimplePoseidonCheckException; 7 | import com.muggle.poseidon.service.TokenService; 8 | import com.muggle.poseidon.store.SecurityStore; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.security.authentication.AuthenticationServiceException; 12 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.security.core.AuthenticationException; 15 | import org.springframework.security.core.userdetails.UserDetails; 16 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 17 | 18 | /** 19 | * @program: poseidon-cloud-starter 20 | * @description: 登陆过滤器 21 | * @author: muggle 22 | * @create: 2019-11-07 23 | **/ 24 | 25 | public class SecurityLoginFilter extends UsernamePasswordAuthenticationFilter { 26 | private TokenService tokenService; 27 | 28 | private SecurityStore securityStore; 29 | 30 | 31 | static Logger logger = LoggerFactory.getLogger(SecurityLoginFilter.class); 32 | 33 | public SecurityLoginFilter(TokenService tokenService, SecurityStore securityStore) { 34 | this.tokenService = tokenService; 35 | this.securityStore = securityStore; 36 | } 37 | 38 | @Override 39 | public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { 40 | try { 41 | UserDetails login = tokenService.login(request, response); 42 | String token = securityStore.signUserMessage(login); 43 | return new UsernamePasswordAuthenticationToken(token, ""); 44 | } catch (SimplePoseidonCheckException e) { 45 | throw new AuthenticationServiceException(e.getMessage()); 46 | } catch (Exception e) { 47 | logger.error("系统异常:", e); 48 | throw new AuthenticationServiceException("系统异常"); 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/filter/SecurityTokenFilter.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.filter; 2 | 3 | 4 | import jakarta.servlet.FilterChain; 5 | import jakarta.servlet.ServletException; 6 | import jakarta.servlet.http.Cookie; 7 | import jakarta.servlet.http.HttpServletRequest; 8 | import jakarta.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | 11 | import com.muggle.poseidon.auto.PoseidonSecurityProperties; 12 | import com.muggle.poseidon.base.exception.BasePoseidonCheckException; 13 | import com.muggle.poseidon.properties.SecurityMessageProperties; 14 | import com.muggle.poseidon.store.SecurityStore; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | import org.springframework.security.access.AccessDeniedException; 18 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 19 | import org.springframework.security.core.context.SecurityContextHolder; 20 | import org.springframework.security.core.userdetails.UserDetails; 21 | import org.springframework.util.AntPathMatcher; 22 | import org.springframework.web.filter.OncePerRequestFilter; 23 | 24 | /** 25 | * @program: poseidon-cloud-starter 26 | * @description: token认证填充用户信息 27 | * @author: muggle 28 | * @create: 2019-11-04 29 | **/ 30 | 31 | public class SecurityTokenFilter extends OncePerRequestFilter { 32 | 33 | private SecurityStore securityStore; 34 | 35 | private AntPathMatcher pathMatcher = new AntPathMatcher(); 36 | 37 | private PoseidonSecurityProperties properties; 38 | 39 | 40 | /** 41 | * logger 42 | */ 43 | private static final Logger log = LoggerFactory.getLogger(SecurityTokenFilter.class); 44 | 45 | public SecurityTokenFilter(SecurityStore securityStore, PoseidonSecurityProperties properties) { 46 | this.properties = properties; 47 | this.securityStore = securityStore; 48 | } 49 | 50 | /** 51 | * 该过滤器会首先从请求头中获取token,如果获取失败则会从cookie 中获取token,key都是 token 52 | * 53 | * @param httpServletRequest 54 | * @param httpServletResponse 55 | * @param filterChain 56 | * @throws ServletException 57 | * @throws IOException 58 | * @throws AccessDeniedException 59 | */ 60 | @Override 61 | protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException, AccessDeniedException { 62 | log.debug(">>>>>>>>>>>>>>>>>>>>>>> 开始校验token <<<<<<<<<<<<<<<<<<<<<"); 63 | String token = httpServletRequest.getHeader("token"); 64 | Cookie[] cookies = httpServletRequest.getCookies(); 65 | String cookieToken = null; 66 | if (cookies != null) { 67 | for (Cookie cookie : cookies) { 68 | if (cookie.getName().equals("token")) { 69 | cookieToken = cookie.getValue(); 70 | } 71 | } 72 | } 73 | if (token == null && cookieToken == null) { 74 | filterChain.doFilter(httpServletRequest, httpServletResponse); 75 | return; 76 | } 77 | 78 | UserDetails userDetails = null; 79 | try { 80 | userDetails = securityStore.getUserdetail(token == null ? cookieToken : token); 81 | } catch (BasePoseidonCheckException e) { 82 | log.error("》》》》 用户凭证为badToken {}", e.getMessage()); 83 | SecurityContextHolder.getContext().setAuthentication(getBadToken(e.getMessage())); 84 | filterChain.doFilter(httpServletRequest, httpServletResponse); 85 | return; 86 | } catch (Exception e) { 87 | log.error("》》》》 token 解析异常:", e); 88 | SecurityContextHolder.getContext().setAuthentication(getBadToken("请求信息非法,无法解析Token")); 89 | filterChain.doFilter(httpServletRequest, httpServletResponse); 90 | return; 91 | } 92 | 93 | if (userDetails == null) { 94 | log.error("该用户不存在, token:{}", token); 95 | SecurityContextHolder.getContext().setAuthentication(getBadToken("该用户不存在,请重新登录")); 96 | filterChain.doFilter(httpServletRequest, httpServletResponse); 97 | return; 98 | } 99 | UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities()); 100 | authenticationToken.setDetails(userDetails); 101 | SecurityContextHolder.getContext().setAuthentication(authenticationToken); 102 | log.debug("》》》》 填充token"); 103 | filterChain.doFilter(httpServletRequest, httpServletResponse); 104 | } 105 | 106 | private UsernamePasswordAuthenticationToken getBadToken(String message) { 107 | UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(SecurityMessageProperties.BAD_TOKEN, null, null); 108 | authenticationToken.setDetails(message); 109 | return authenticationToken; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/handler/query/QuerySqlProcessor.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.handler.query; 2 | 3 | import com.muggle.poseidon.base.BaseQuery; 4 | import com.muggle.poseidon.base.ResultBean; 5 | 6 | /** 7 | * @Description: 8 | * @Author: muggle 9 | * @Date: 2020/6/8 10 | **/ 11 | public interface QuerySqlProcessor { 12 | 13 | /** 14 | * 返回值处理逻辑 15 | * 16 | * @param query 17 | */ 18 | void afterReturningQuery(ResultBean query); 19 | 20 | /** 21 | * aop执行,调用自定义query处理逻辑 22 | * 23 | * @param query 24 | */ 25 | void beforeQuery(BaseQuery query); 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/handler/security/PoseidonAccessDeniedHandler.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.handler.security; 2 | 3 | import jakarta.servlet.ServletException; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | 9 | import com.muggle.poseidon.properties.SecurityMessageProperties; 10 | import org.apache.commons.logging.Log; 11 | import org.apache.commons.logging.LogFactory; 12 | import org.springframework.security.access.AccessDeniedException; 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.security.core.context.SecurityContextHolder; 15 | import org.springframework.security.core.userdetails.UserDetails; 16 | import org.springframework.security.web.access.AccessDeniedHandler; 17 | 18 | /** 19 | * @program: poseidon 20 | * @description: 403处理器 AuthenticationFailureHandler 21 | * @author: muggle 22 | * @create: 2018-12-27 19:21 23 | **/ 24 | public class PoseidonAccessDeniedHandler implements AccessDeniedHandler { 25 | private static final Log log = LogFactory.getLog(PoseidonAccessDeniedHandler.class); 26 | 27 | @Override 28 | public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException { 29 | log.error("用户沒有权限访问 路径: " + request.getRequestURI()); 30 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 31 | Object details = authentication.getDetails(); 32 | 33 | if (SecurityMessageProperties.BAD_TOKEN.equals(authentication.getPrincipal())) { 34 | response.setContentType("application/json;charset=UTF-8"); 35 | PrintWriter writer = response.getWriter(); 36 | writer.write("{\"code\":403,\"message\":\"" + details.toString() + "\"}"); 37 | writer.close(); 38 | return; 39 | } 40 | if (details instanceof UserDetails) { 41 | if (!((UserDetails) details).isEnabled()) { 42 | response.setContentType("application/json;charset=UTF-8"); 43 | PrintWriter writer = response.getWriter(); 44 | writer.write("{\"code\":403,\"message\":\"账号过期\"}"); 45 | writer.close(); 46 | return; 47 | } 48 | if (!((UserDetails) details).isAccountNonLocked()) { 49 | response.setContentType("application/json;charset=UTF-8"); 50 | PrintWriter writer = response.getWriter(); 51 | writer.write("{\"code\":403,\"message\":\"账号被锁定\"}"); 52 | writer.close(); 53 | return; 54 | } else { 55 | response.setContentType("application/json;charset=UTF-8"); 56 | PrintWriter writer = response.getWriter(); 57 | writer.write("{\"code\":403,\"message\":\"没有权限\"}"); 58 | writer.close(); 59 | return; 60 | } 61 | 62 | } else { 63 | response.setContentType("application/json;charset=UTF-8"); 64 | PrintWriter writer = response.getWriter(); 65 | writer.write("{\"code\":403,\"message\":\"没有权限\"}"); 66 | writer.close(); 67 | return; 68 | } 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/handler/security/PoseidonAuthenticationFailureHandler.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.handler.security; 2 | 3 | import jakarta.servlet.ServletException; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.security.core.AuthenticationException; 12 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 13 | 14 | /** 15 | * 登录验证失败处理器 16 | */ 17 | public class PoseidonAuthenticationFailureHandler implements AuthenticationFailureHandler { 18 | private static final Logger log = LoggerFactory.getLogger(PoseidonAuthenticationFailureHandler.class); 19 | 20 | @Override 21 | public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { 22 | httpServletResponse.setContentType("application/json;charset=UTF-8"); 23 | final PrintWriter writer = httpServletResponse.getWriter(); 24 | writer.write("{\"code\":5001,\"message\":\"" + e.getMessage() + "\"}"); 25 | writer.close(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/handler/security/PoseidonAuthenticationSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.handler.security; 2 | 3 | import jakarta.servlet.ServletException; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import com.muggle.poseidon.base.ResultBean; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; 15 | 16 | public class PoseidonAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { 17 | private static final Logger log = LoggerFactory.getLogger(PoseidonAuthenticationSuccessHandler.class); 18 | 19 | @Override 20 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { 21 | Object principal = authentication.getPrincipal(); 22 | log.debug("登录成功!"); 23 | response.setContentType("application/json;charset=UTF-8"); 24 | final PrintWriter writer = response.getWriter(); 25 | ObjectMapper objectMapper = new ObjectMapper(); 26 | String result = objectMapper.writeValueAsString(ResultBean.successData(principal)); 27 | writer.write(result); 28 | writer.close(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/handler/security/PoseidonLoginUrlAuthenticationEntryPoint.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.handler.security; 2 | 3 | import jakarta.servlet.ServletException; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | 9 | import org.apache.commons.logging.Log; 10 | import org.apache.commons.logging.LogFactory; 11 | import org.springframework.security.core.AuthenticationException; 12 | import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; 13 | 14 | /** 15 | * @program: poseidon 16 | * @description: 未登录处理 17 | * @author: muggle 18 | * @create: 2018-12-31 19 | **/ 20 | public class PoseidonLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { 21 | private static final Log log = LogFactory.getLog(PoseidonLoginUrlAuthenticationEntryPoint.class); 22 | 23 | public PoseidonLoginUrlAuthenticationEntryPoint(String loginFormUrl) { 24 | super(loginFormUrl); 25 | } 26 | 27 | @Override 28 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { 29 | log.error(">>>>>>>>>>>>>>>>>>>>> 用户未登陆 <<<<<<<<<<<<<<<<<<<<<<<<<"); 30 | response.setContentType("application/json;charset=UTF-8"); 31 | final PrintWriter writer = response.getWriter(); 32 | writer.write("{\"code\":401,\"message\":\"用户未登录\"}"); 33 | writer.close(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/handler/security/PoseidonLogoutSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.handler.security; 2 | 3 | import jakarta.servlet.ServletException; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | 9 | import com.muggle.poseidon.store.SecurityStore; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; 14 | 15 | /** 16 | * @program: poseidon 17 | * @description: 登出成功处理器 18 | * @author: muggle 19 | * @create: 2018-12-31 20 | **/ 21 | public class PoseidonLogoutSuccessHandler implements LogoutSuccessHandler { 22 | 23 | 24 | private SecurityStore securityStore; 25 | 26 | public PoseidonLogoutSuccessHandler(SecurityStore securityStore) { 27 | this.securityStore = securityStore; 28 | } 29 | 30 | private final static Logger log = LoggerFactory.getLogger("PoseidonLogoutSuccessHandler"); 31 | 32 | @Override 33 | public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { 34 | response.setContentType("application/json;charset=UTF-8"); 35 | PrintWriter writer = response.getWriter(); 36 | if (authentication == null) { 37 | writer.write("{\"code\":401,\"message\":\"用户未登录\"}"); 38 | writer.close(); 39 | return; 40 | } 41 | Object principal = authentication.getPrincipal(); 42 | try { 43 | Boolean success = securityStore.cleanToken((String) principal); 44 | if (success) { 45 | log.info("用户登出, token: {}", principal); 46 | writer.write("{\"code\":200,\"message\":\"登出成功\"}"); 47 | } else { 48 | log.info("用户登出失败 token:{}", principal); 49 | writer.write("{\"code\":5001,\"message\":\"登出失败,请重试\"}"); 50 | } 51 | } catch (Exception e) { 52 | log.error("登出失败: ", e); 53 | writer.write("{\"code\":\"5001\",\"message\":\"" + e.getMessage() + "\"}"); 54 | } finally { 55 | writer.close(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/handler/security/RequestLogProcessor.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.handler.security; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | 5 | public interface RequestLogProcessor { 6 | /** 7 | * 日志记录,进入controller之前 只可读,所以用final 修饰 8 | * 9 | * @param request 10 | * @param args 11 | */ 12 | void recordBefore(final HttpServletRequest request, final Object[] args); 13 | 14 | /** 15 | * 日志记录,进入controller 获取返回值 result 为返回值,只可读。 16 | * 17 | * @param result 返回值 18 | * @param args 入参 19 | */ 20 | void recordAfterReturning(final Object result, final Object[] args); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/handler/web/RoolMessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.handler.web; 2 | 3 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 4 | 5 | /** 6 | * @program: poseidon-cloud-starter 7 | * @description: 权限信息处理器 8 | * 废弃原因——加入到开机任务当中 9 | * @author: muggle 10 | * @create: 2019-11-05 11 | **/ 12 | @Deprecated 13 | public class RoolMessageHandler { 14 | 15 | private RequestMappingHandlerMapping handlerMapping; 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/handler/web/WebResultHandler.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.handler.web; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | 5 | import com.muggle.poseidon.base.ResultBean; 6 | import com.muggle.poseidon.base.exception.BasePoseidonCheckException; 7 | import com.muggle.poseidon.base.exception.BasePoseidonException; 8 | import com.muggle.poseidon.listener.ExceptionEvent; 9 | import com.muggle.poseidon.util.UserInfoUtils; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 14 | import org.springframework.context.ApplicationContext; 15 | import org.springframework.security.core.userdetails.UserDetails; 16 | import org.springframework.validation.BindException; 17 | import org.springframework.web.HttpRequestMethodNotSupportedException; 18 | import org.springframework.web.bind.MethodArgumentNotValidException; 19 | import org.springframework.web.bind.annotation.ExceptionHandler; 20 | import org.springframework.web.bind.annotation.RestControllerAdvice; 21 | import org.springframework.web.servlet.NoHandlerFoundException; 22 | 23 | @RestControllerAdvice 24 | @ConditionalOnProperty(prefix = "poseidon", name = "auto", havingValue = "true", matchIfMissing = false) 25 | public class WebResultHandler { 26 | 27 | public WebResultHandler() { 28 | log.debug(">>>>>>>>>>>>>>>>>>>>> WebResultHandler 注册 <<<<<<<<<<<<<<<<<<<<"); 29 | } 30 | 31 | private static final Logger log = LoggerFactory.getLogger(WebResultHandler.class); 32 | 33 | @Autowired 34 | ApplicationContext applicationContext; 35 | 36 | 37 | /** 38 | * 自定义异常 39 | * 40 | * @param e 41 | * @param req 42 | * @return 43 | */ 44 | @ExceptionHandler(value = {BasePoseidonException.class}) 45 | public ResultBean poseidonExceptionHandler(BasePoseidonException e, HttpServletRequest req) { 46 | log.error("业务异常,错误码:{}", e.getCode(), e); 47 | ResultBean error = ResultBean.error(e.getMessage(), e.getCode() == null ? 5001 : e.getCode()); 48 | return error; 49 | } 50 | 51 | /** 52 | * 参数未通过校验 53 | * 54 | * @param e 55 | * @param req 56 | * @return 57 | */ 58 | @ExceptionHandler(value = {MethodArgumentNotValidException.class}) 59 | public ResultBean methodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest req) { 60 | log.error("参数未通过校验", e); 61 | ResultBean error = ResultBean.error(e.getBindingResult().getFieldError().getDefaultMessage()); 62 | return error; 63 | } 64 | 65 | /** 66 | * BeanPropertyBindingResult 67 | */ 68 | @ExceptionHandler(value = {BindException.class}) 69 | public ResultBean beanPropertyBindingResult(BindException e, HttpServletRequest req) { 70 | log.error("参数未通过校验", e); 71 | ResultBean error = ResultBean.error(e.getFieldError().getDefaultMessage()); 72 | return error; 73 | } 74 | 75 | /** 76 | * 错误的请求方式 77 | * 78 | * @param e 79 | * @param req 80 | * @return 81 | */ 82 | @ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class}) 83 | public ResultBean notsupported(Exception e, HttpServletRequest req) { 84 | log.error("错误的请求方式", e); 85 | ResultBean error = ResultBean.error("错误的请求方式"); 86 | return error; 87 | } 88 | 89 | /** 90 | * 请求路径不存在 91 | * 92 | * @param e 93 | * @param req 94 | * @return 95 | */ 96 | @ExceptionHandler(value = {NoHandlerFoundException.class}) 97 | public ResultBean notFoundUrl(Exception e, HttpServletRequest req) { 98 | log.error("请求路径不存在", e); 99 | ResultBean error = ResultBean.error("请求路径不存在"); 100 | return error; 101 | } 102 | 103 | 104 | /** 105 | * 自定义异常 106 | * 107 | * @param e 108 | * @param req 109 | * @return 110 | */ 111 | @ExceptionHandler(value = {BasePoseidonCheckException.class}) 112 | public ResultBean checked(BasePoseidonCheckException e, HttpServletRequest req) { 113 | log.error("自定义异常,错误码:{}", e.getCode(), e); 114 | ResultBean error = ResultBean.error(e.getMessage(), e.getCode() == null ? 5001 : e.getCode()); 115 | return error; 116 | } 117 | 118 | /** 119 | * 未知异常,需要通知到管理员,对于线上未知的异常,我们应该严肃处理:先将消息传给MQ中心(该平台未实现) 然后日志写库 120 | * 这里的处理方式是抛出事件 121 | * 122 | * @param e 123 | * @param req 124 | * @return 125 | */ 126 | @ExceptionHandler(value = {Exception.class}) 127 | public ResultBean exceptionHandler(Exception e, HttpServletRequest req) { 128 | try { 129 | UserDetails userInfo = UserInfoUtils.getUserInfo(); 130 | log.error("系统异常:" + req.getMethod() + req.getRequestURI() + " user: " + (userInfo == null ? "无用户信息" : userInfo.toString()), e); 131 | ExceptionEvent exceptionEvent = new ExceptionEvent(String.format("系统异常: [ %s ] 时间戳: [%d] ", e.getMessage(), System.currentTimeMillis()), this); 132 | applicationContext.publishEvent(exceptionEvent); 133 | return ResultBean.error("系统异常", 500); 134 | } catch (Exception err) { 135 | log.error("紧急!!! 严重的异常", err); 136 | return ResultBean.error("系统发生严重的错误", 500); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/handler/web/WebUrlHandler.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.handler.web; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | 5 | import com.muggle.poseidon.base.ResultBean; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 10 | import org.springframework.boot.web.servlet.error.ErrorController; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | /** 15 | * @program: poseidon-cloud-core 16 | * @description: 17 | * @author: muggle 18 | * @create: 2020-03-12 09:54 19 | */ 20 | @RestController 21 | @ConditionalOnProperty(prefix = "poseidon", name = "auto", havingValue = "true", matchIfMissing = false) 22 | public class WebUrlHandler implements ErrorController { 23 | @Value("${spring.application.name:poseidon-boot-starter}") 24 | private String appName; 25 | 26 | public WebUrlHandler() { 27 | log.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>> WebUrlHandler 注册 <<<<<<<<<<<<<<<<<<<<"); 28 | } 29 | 30 | private static final Logger log = LoggerFactory.getLogger(WebUrlHandler.class); 31 | 32 | @RequestMapping(value = "/public/notfound", produces = "application/json;charset=UTF-8") 33 | public ResultBean notfund(HttpServletRequest request) { 34 | log.debug(">>>>>>>>>>>>>>>>>>>>>>>> 客户端访问了错误的页面 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); 35 | return ResultBean.error("找不到页面", 404); 36 | } 37 | 38 | @Override 39 | public String getErrorPath() { 40 | return "/error_message"; 41 | } 42 | 43 | 44 | @RequestMapping("/") 45 | public ResultBean getMessage() { 46 | log.debug("请求发起 访问路径: /"); 47 | 48 | return ResultBean.successData(appName); 49 | } 50 | 51 | @RequestMapping(value = "/error", produces = "application/json;charset=UTF-8") 52 | public ResultBean error(HttpServletRequest request) { 53 | log.error(">>>>>>>>>>>>>>>>>>> error <<<<<<<<<<<<<<<<<<<<<<<<"); 54 | return ResultBean.error("请求未响应,请稍后再试"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/listener/ExceptionEvent.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.listener; 2 | 3 | import org.springframework.context.ApplicationEvent; 4 | 5 | /** 6 | * @Description: 7 | * @Author: muggle 8 | * @Date: 2020/4/2$ 9 | **/ 10 | public class ExceptionEvent extends ApplicationEvent { 11 | private String message; 12 | 13 | /** 14 | * Create a new ApplicationEvent. 15 | * 16 | * @param source the object on which the event initially occurred (never {@code null}) 17 | */ 18 | public ExceptionEvent(String message, Object source) { 19 | super(source); 20 | this.message = message; 21 | } 22 | 23 | public String getMessage() { 24 | return this.message; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/listener/ExceptionListener.java: -------------------------------------------------------------------------------- 1 | //package com.muggle.poseidon.listener; 2 | // 3 | //import com.muggle.poseidon.properties.DingParamProperties; 4 | //import com.muggle.poseidon.properties.DingSendEnum; 5 | //import com.muggle.poseidon.util.DingUtil; 6 | //import org.springframework.context.ApplicationListener; 7 | //import org.springframework.stereotype.Component; 8 | // 9 | ///** 10 | // * @Description: 11 | // * @Author: muggle 12 | // * @Date: 2020/4/2$ 13 | // **/ 14 | // 15 | // 16 | //@Component 17 | //public class ExceptionListener implements ApplicationListener { 18 | // 19 | // 20 | // @Override 21 | // public void onApplicationEvent(ExceptionEvent event) { 22 | // // todo 23 | // int i=0; 24 | // while (i<3) { 25 | // try { 26 | // String message = event.getMessage(); 27 | // break; 28 | // } catch (Exception e) { 29 | // i++; 30 | // } 31 | // } 32 | // } 33 | //} 34 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/manager/PoseidonFilterInvocationSecurityMetadataSource.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.manager; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import java.util.Collection; 5 | 6 | import org.springframework.security.access.ConfigAttribute; 7 | import org.springframework.security.web.FilterInvocation; 8 | import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; 9 | 10 | /** 11 | * @program: poseidon 12 | * @description: URL权限填充 13 | * @author: muggle 14 | * @create: 2018-12-30 11:39 15 | **/ 16 | 17 | public class PoseidonFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { 18 | 19 | 20 | @Override 21 | public Collection getAttributes(Object object) throws IllegalArgumentException { 22 | final HttpServletRequest httpRequest = ((FilterInvocation) object).getHttpRequest(); 23 | return null; 24 | } 25 | 26 | @Override 27 | public Collection getAllConfigAttributes() { 28 | return null; 29 | } 30 | 31 | @Override 32 | public boolean supports(Class aClass) { 33 | return true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/manager/PoseidonWebExpressionVoter.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.manager; 2 | 3 | import java.util.Collection; 4 | 5 | import com.muggle.poseidon.auto.PoseidonSecurityProperties; 6 | import com.muggle.poseidon.properties.SecurityMessageProperties; 7 | import com.muggle.poseidon.service.TokenService; 8 | import com.muggle.poseidon.store.SecurityStore; 9 | import org.apache.commons.logging.Log; 10 | import org.apache.commons.logging.LogFactory; 11 | import org.springframework.security.access.ConfigAttribute; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.security.core.GrantedAuthority; 14 | import org.springframework.security.core.userdetails.UserDetails; 15 | import org.springframework.security.web.FilterInvocation; 16 | import org.springframework.security.web.access.expression.WebExpressionVoter; 17 | import org.springframework.util.AntPathMatcher; 18 | 19 | /** 20 | * @program: poseidon-cloud-starter 21 | * @description: 投票器 22 | * @author: muggle 23 | * @create: 2019-11-05 24 | **/ 25 | 26 | public class PoseidonWebExpressionVoter extends WebExpressionVoter { 27 | 28 | private static final Log log = LogFactory.getLog(PoseidonWebExpressionVoter.class); 29 | 30 | private static AntPathMatcher PATH_MATCHER = new AntPathMatcher(); 31 | 32 | private TokenService tokenService; 33 | 34 | private PoseidonSecurityProperties properties; 35 | 36 | public PoseidonWebExpressionVoter(TokenService tokenService, PoseidonSecurityProperties properties) { 37 | this.properties = properties; 38 | this.tokenService = tokenService; 39 | } 40 | 41 | /** 42 | * @throws 43 | * @author muggle 44 | * @Description: 权限校验方法(投票器) 1通过,-1不通过 0弃权 45 | * @Param: fixme 46 | * @return: 47 | * @date 2019/11/5 11:21 48 | */ 49 | @Override 50 | public int vote(Authentication authentication, FilterInvocation fi, Collection attributes) { 51 | String requestUrl = fi.getRequestUrl(); 52 | log.debug("》》》》 请求进入, url:【" + requestUrl + "】用户名:【" + authentication.getPrincipal() + "】"); 53 | /** 获取所有放行权限(在adapter中的config 方法配置)*/ 54 | for (String pattern : 55 | SecurityStore.ACCESS_PATHS) { 56 | boolean match = PATH_MATCHER.match(pattern, requestUrl); 57 | if (match) { 58 | return ACCESS_GRANTED; 59 | } 60 | } 61 | for (String pattern : 62 | properties.getIgnorePath()) { 63 | boolean match = PATH_MATCHER.match(pattern, requestUrl); 64 | if (match) { 65 | return ACCESS_GRANTED; 66 | } 67 | } 68 | boolean bool = tokenVerify(authentication); 69 | // 未携带token 或者token失效 用户被冻结 用户 70 | if (bool) { 71 | return ACCESS_DENIED; 72 | } 73 | /** 获取用户角色,并通过角色去匹配权限*/ 74 | Collection authorities = authentication.getAuthorities(); 75 | 76 | boolean b = tokenService.rooleMatch(authorities, requestUrl); 77 | if (b) { 78 | return ACCESS_GRANTED; 79 | } 80 | log.debug("》》》》 用户无权限访问,用户名:【" + authentication.getPrincipal() + "】"); 81 | return ACCESS_DENIED; 82 | } 83 | 84 | /** 85 | * 验证用户是否已登陆,账号是否被锁定或者被冻结 86 | * 87 | * @param authentication 88 | * @return 89 | */ 90 | private boolean tokenVerify(Authentication authentication) { 91 | boolean equals = SecurityMessageProperties.BAD_TOKEN.equals(authentication.getPrincipal()); 92 | Object details = authentication.getDetails(); 93 | if (details == null || !(details instanceof UserDetails)) { 94 | return true; 95 | } 96 | UserDetails userInfo = (UserDetails) details; 97 | boolean accountNonLocked = userInfo.isAccountNonLocked(); 98 | boolean enabled = userInfo.isEnabled(); 99 | return equals || !accountNonLocked || !enabled; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/properties/SecurityMessageProperties.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.properties; 2 | 3 | /** 4 | * @program: poseidon-cloud-starter 5 | * @description: 6 | * @author: muggle 7 | * @create: 2019-11-05 8 | **/ 9 | 10 | public interface SecurityMessageProperties { 11 | String BAD_TOKEN = "BAD_TOKEN"; 12 | String SUBJECT = "POSEIDON_CLAIM"; 13 | String ISSUER = "security"; 14 | String RANDOM = "RANDOM"; 15 | String LOGOUT = "/logout"; 16 | String LOGIN_URL = "/sign_in"; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/service/TokenService.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.service; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import jakarta.servlet.http.HttpServletResponse; 5 | import java.util.Collection; 6 | import java.util.List; 7 | 8 | import com.muggle.poseidon.base.exception.SimplePoseidonCheckException; 9 | import com.muggle.poseidon.entity.AuthUrlPathDO; 10 | import org.springframework.security.core.GrantedAuthority; 11 | import org.springframework.security.core.userdetails.UserDetails; 12 | import org.springframework.security.core.userdetails.UserDetailsService; 13 | 14 | public interface TokenService extends UserDetailsService { 15 | 16 | UserDetails getUserById(Long id); 17 | 18 | /** 19 | * @author muggle 20 | * @Description: 权限匹配接口 21 | * @Param: path为请求的 url roleCodes 权限编码集合 22 | * @return: 23 | * @date 2019/11/5 18:05 24 | */ 25 | boolean rooleMatch(Collection authorities, String path); 26 | 27 | /** 28 | * 项目初始化url处理方法 29 | * 30 | * @param list 31 | */ 32 | void processUrl(List list); 33 | 34 | /** 35 | * 登录方法(可以自定义登录逻辑,比如加验证码等等) 36 | * 37 | * @param request 38 | * @param response 39 | * @return 40 | * @throws 41 | */ 42 | UserDetails login(HttpServletRequest request, HttpServletResponse response) throws SimplePoseidonCheckException; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/store/SecurityStore.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.store; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.CopyOnWriteArrayList; 5 | 6 | import com.muggle.poseidon.base.exception.BasePoseidonCheckException; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | 9 | public interface SecurityStore { 10 | 11 | List ACCESS_PATHS = new CopyOnWriteArrayList(); 12 | 13 | 14 | /** 15 | * @throws 16 | * @author muggle 17 | * @Description: 根据token获取用户信息,先通过凭证解析token,然后获得storeKey,random,,自定义body,然后根据key 和random(版本号)获取用户信息 18 | * @Param: 19 | * @return: 20 | * @date 2019/11/5 11:40 21 | */ 22 | UserDetails getUserdetail(String token) throws BasePoseidonCheckException; 23 | 24 | /** 25 | * @throws 26 | * @author muggle 27 | * @Description: 将用户的登陆信息保存到token仓库中(比如Redis 或者mysql) 28 | * @Param: 29 | * @return: 30 | * @date 2019/11/5 11:40 31 | */ 32 | String signUserMessage(UserDetails userDetails); 33 | 34 | /** 35 | * @author muggle 36 | * @Description: 清除用户登录信息(登出) 37 | * @Param: 38 | * @return: 39 | * @date 2019/11/5 11:42 40 | */ 41 | Boolean cleanToken(String username); 42 | 43 | /** 44 | * @author muggle 45 | * @Description: 保存放行的url 46 | * @Param: 47 | * @return: 48 | * @date 2019/11/5 11:44 49 | */ 50 | static void saveAccessPath(List paths) { 51 | ACCESS_PATHS.addAll(paths); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/store/impl/SimpleRedisSecurityStore.java: -------------------------------------------------------------------------------- 1 | //package com.muggle.poseidon.store.impl; 2 | // 3 | //import com.muggle.poseidon.store.SecurityStore; 4 | //import org.springframework.cglib.core.internal.Function; 5 | //import org.springframework.data.redis.core.RedisTemplate; 6 | //import org.springframework.security.core.userdetails.UserDetails; 7 | // 8 | //import java.util.List; 9 | //import java.util.concurrent.TimeUnit; 10 | // 11 | ///** 12 | // * @program: poseidon-cloud-starter 13 | // * @description: redis存储token的仓库 14 | // * @author: muggle 15 | // * @create: 2019-11-04 16 | // **/ 17 | // 18 | //public class SimpleRedisSecurityStore implements SecurityStore { 19 | // private RedisTemplate redisTemplate; 20 | // 21 | // 22 | // 23 | // @Override 24 | // public UserDetails getUserdetail(String token) { 25 | // UserDetails userDetails = redisTemplate.opsForValue().get(token); 26 | // return userDetails; 27 | // } 28 | // 29 | // @Override 30 | // public void saveUser(UserDetails userDetails, long expirationTime, String token) { 31 | // redisTemplate.opsForValue().set(token,userDetails,expirationTime); 32 | // } 33 | // 34 | // @Override 35 | // public Boolean cleanToken(String token) { 36 | // Boolean delete = redisTemplate.delete(token); 37 | // return delete; 38 | // } 39 | // 40 | // @Override 41 | // public void setExpirationTime(long expirationTime, String token) { 42 | // redisTemplate.expire(token,expirationTime, TimeUnit.MILLISECONDS); 43 | // } 44 | // 45 | // @Override 46 | // public UserDetails processToken(UserDetails userDetails, String token, Function function) { 47 | // return null; 48 | // } 49 | // 50 | //} 51 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/BaseDaoUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | /** 4 | * @Description: 5 | * @Author: muggle 6 | * @Date: 2020/8/27 7 | **/ 8 | public class BaseDaoUtils { 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/DateUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import java.text.DateFormat; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.ArrayList; 7 | import java.util.Calendar; 8 | import java.util.Date; 9 | import java.util.List; 10 | 11 | /** 12 | * @Description: 13 | * @Author: muggle 14 | * @Date: 2020/7/29 15 | **/ 16 | public class DateUtils { 17 | public static final String DATE_PATTERN = "yyyy-MM-dd"; 18 | public static final String DATE_PATTERN1 = "yyyy_MM_dd"; 19 | public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; 20 | public static final String TIME_PATTERN = "HH:mm:ss"; 21 | 22 | public DateUtils() { 23 | } 24 | 25 | public static String format(long millis, String pattern) { 26 | return format(new Date(millis), pattern); 27 | } 28 | 29 | public static long getDateMinus(String startDate, String endDate, String pattern) { 30 | SimpleDateFormat myFormatter = new SimpleDateFormat(pattern); 31 | 32 | try { 33 | Date date = myFormatter.parse(startDate); 34 | Date mydate = myFormatter.parse(endDate); 35 | long day = (mydate.getTime() - date.getTime()) / 60000L; 36 | return day; 37 | } catch (ParseException var8) { 38 | var8.printStackTrace(); 39 | return 0L; 40 | } 41 | } 42 | 43 | public static String addDay(String s, int n) { 44 | try { 45 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 46 | Calendar cd = Calendar.getInstance(); 47 | cd.setTime(sdf.parse(s)); 48 | cd.add(5, n); 49 | return sdf.format(cd.getTime()); 50 | } catch (Exception var4) { 51 | return null; 52 | } 53 | } 54 | 55 | public static int getDaysByYearMonth(String dateString) { 56 | int year = Integer.parseInt(dateString.substring(0, 4)); 57 | int month = Integer.parseInt(dateString.substring(5, 7)); 58 | Calendar a = Calendar.getInstance(); 59 | a.set(1, year); 60 | a.set(2, month - 1); 61 | a.set(5, 1); 62 | a.roll(5, -1); 63 | int maxDate = a.get(5); 64 | return maxDate; 65 | } 66 | 67 | public static String getLastDayOfMonth(int year, int month) { 68 | Calendar cal = Calendar.getInstance(); 69 | cal.set(1, year); 70 | cal.set(2, month - 1); 71 | int lastDay = cal.getActualMaximum(5); 72 | cal.set(5, lastDay); 73 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 74 | String lastDayOfMonth = sdf.format(cal.getTime()); 75 | return lastDayOfMonth; 76 | } 77 | 78 | public static Date ConverToDate(String strDate) { 79 | SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); 80 | 81 | try { 82 | return df.parse(strDate); 83 | } catch (ParseException var3) { 84 | var3.printStackTrace(); 85 | return null; 86 | } 87 | } 88 | 89 | public static List findDates(String startTime, String endTime) { 90 | Date startDate = ConverToDate(startTime); 91 | Date endDate = ConverToDate(endTime); 92 | List lDate = new ArrayList(); 93 | lDate.add(startDate); 94 | Calendar calBegin = Calendar.getInstance(); 95 | calBegin.setTime(startDate); 96 | Calendar calEnd = Calendar.getInstance(); 97 | calEnd.setTime(endDate); 98 | 99 | while (endDate.after(calBegin.getTime())) { 100 | calBegin.add(5, 1); 101 | lDate.add(calBegin.getTime()); 102 | } 103 | 104 | return lDate; 105 | } 106 | 107 | public static int getWeek(Date date) { 108 | int[] weeks = new int[]{0, 1, 2, 3, 4, 5, 6}; 109 | Calendar cal = Calendar.getInstance(); 110 | cal.setTime(date); 111 | int week_index = cal.get(7) - 1; 112 | if (week_index < 0) { 113 | week_index = 0; 114 | } 115 | 116 | return weeks[week_index]; 117 | } 118 | 119 | public static String getTimeDifference(String startTime, String endTime) { 120 | SimpleDateFormat dfs = new SimpleDateFormat("HH:mm:ss"); 121 | long between = 0L; 122 | 123 | try { 124 | Date begin = dfs.parse(startTime); 125 | Date end = dfs.parse(endTime); 126 | between = end.getTime() - begin.getTime(); 127 | } catch (Exception var14) { 128 | var14.printStackTrace(); 129 | } 130 | 131 | long day = between / 86400000L; 132 | long hour = between / 3600000L - day * 24L; 133 | long min = between / 60000L - day * 24L * 60L - hour * 60L; 134 | long s = between / 1000L - day * 24L * 60L * 60L - hour * 60L * 60L - min * 60L; 135 | String date = day + "天" + hour + "小时" + min + "分" + s + "秒"; 136 | if (day == 0L && hour != 0L) { 137 | date = hour + "小时" + min + "分" + s + "秒"; 138 | } else if (day == 0L && hour == 0L && min != 0L) { 139 | date = min + "分" + s + "秒"; 140 | } else if (day == 0L && hour == 0L && min == 0L) { 141 | date = s + "秒"; 142 | } 143 | 144 | return date; 145 | } 146 | 147 | public static int getNextDaySec() { 148 | Date date = new Date(); 149 | Calendar cal = Calendar.getInstance(); 150 | cal.setTime(date); 151 | cal.add(6, 1); 152 | cal.set(11, 9); 153 | cal.set(12, 0); 154 | cal.set(13, 0); 155 | Long tm = cal.getTimeInMillis() - date.getTime(); 156 | return (int) (tm / 1000L); 157 | } 158 | 159 | public static String getWeekOfDate(Date date) { 160 | String[] weekOfDays = new String[]{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"}; 161 | Calendar cal = Calendar.getInstance(); 162 | cal.setTime(date); 163 | int week_index = cal.get(7) - 1; 164 | if (week_index < 0) { 165 | week_index = 0; 166 | } 167 | 168 | return weekOfDays[week_index]; 169 | } 170 | 171 | public static String format(Date date, String pattern) { 172 | DateFormat formatter = new SimpleDateFormat(pattern); 173 | return formatter.format(date); 174 | } 175 | 176 | public static String formatDate(Date date) { 177 | return format(date, "yyyy-MM-dd"); 178 | } 179 | 180 | public static String formatTime(Date date) { 181 | return format(date, "HH:mm:ss"); 182 | } 183 | 184 | public static String formatDateTime(Date date) { 185 | return format(date, "yyyy-MM-dd HH:mm:ss"); 186 | } 187 | 188 | public static String formatCurrent(String pattern) { 189 | return format(new Date(), pattern); 190 | } 191 | 192 | public static String formatCurrentDate() { 193 | return format(new Date(), "yyyy-MM-dd"); 194 | } 195 | 196 | public static String formatCurrentTime() { 197 | return format(new Date(), "HH:mm:ss"); 198 | } 199 | 200 | public static String formatCurrentDateTime() { 201 | return format(new Date(), "yyyy-MM-dd HH:mm:ss"); 202 | } 203 | 204 | public static Date getCurrentDate() { 205 | Calendar cal = Calendar.getInstance(); 206 | cal.set(11, 0); 207 | cal.set(12, 0); 208 | cal.set(13, 0); 209 | cal.set(14, 0); 210 | return cal.getTime(); 211 | } 212 | 213 | public static Date getTheDate(Date date) { 214 | Calendar cal = Calendar.getInstance(); 215 | cal.setTime(date); 216 | cal.set(11, 0); 217 | cal.set(12, 0); 218 | cal.set(13, 0); 219 | cal.set(14, 0); 220 | return cal.getTime(); 221 | } 222 | 223 | public static int compareDate(Date start, Date end) { 224 | if (start == null && end == null) { 225 | return 0; 226 | } else if (end == null) { 227 | return 1; 228 | } else { 229 | if (start == null) { 230 | start = new Date(); 231 | } 232 | 233 | start = getTheDate(start); 234 | end = getTheDate(end); 235 | return start.compareTo(end); 236 | } 237 | } 238 | 239 | public static Date stringToDate(String dateString, String pattern) { 240 | SimpleDateFormat formatter = new SimpleDateFormat(pattern); 241 | 242 | try { 243 | return formatter.parse(dateString); 244 | } catch (ParseException var4) { 245 | throw new RuntimeException(var4); 246 | } 247 | } 248 | 249 | public static Date addYears(Date date, int amount) { 250 | return add(date, 1, amount); 251 | } 252 | 253 | public static Date addMonths(Date date, int amount) { 254 | return add(date, 2, amount); 255 | } 256 | 257 | public static Date addWeeks(Date date, int amount) { 258 | return add(date, 3, amount); 259 | } 260 | 261 | public static Date addDays(Date date, int amount) { 262 | return add(date, 5, amount); 263 | } 264 | 265 | public static Date addHours(Date date, int amount) { 266 | return add(date, 11, amount); 267 | } 268 | 269 | public static Date addMinutes(Date date, int amount) { 270 | return add(date, 12, amount); 271 | } 272 | 273 | public static Date addSeconds(Date date, int amount) { 274 | return add(date, 13, amount); 275 | } 276 | 277 | public static Date addMilliseconds(Date date, int amount) { 278 | return add(date, 14, amount); 279 | } 280 | 281 | private static Date add(Date date, int calendarField, int amount) { 282 | if (date == null) { 283 | throw new IllegalArgumentException("日期对象不允许为null!"); 284 | } else { 285 | Calendar c = Calendar.getInstance(); 286 | c.setTime(date); 287 | c.add(calendarField, amount); 288 | return c.getTime(); 289 | } 290 | } 291 | 292 | public static Date getDateAfterNatureDays(Date date, int days, int hours) { 293 | if (date == null) { 294 | date = new Date(); 295 | } 296 | 297 | Calendar c = Calendar.getInstance(); 298 | c.setTime(date); 299 | c.add(5, days); 300 | c.add(10, hours); 301 | return c.getTime(); 302 | } 303 | 304 | public static Date getDateAfterWorkDays(Date date, int days, int hours) { 305 | if (date == null) { 306 | date = new Date(); 307 | } 308 | 309 | Calendar c = Calendar.getInstance(); 310 | c.setTime(date); 311 | c.add(5, days); 312 | c.add(10, hours); 313 | return c.getTime(); 314 | } 315 | 316 | public static String dateToStamp(String s) throws ParseException { 317 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 318 | Date date = simpleDateFormat.parse(s); 319 | long ts = date.getTime(); 320 | String res = String.valueOf(ts); 321 | return res; 322 | } 323 | 324 | public static String stampToDate(String s) { 325 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 326 | long lt = new Long(s); 327 | Date date = new Date(lt); 328 | return simpleDateFormat.format(date); 329 | } 330 | 331 | public static void main(String[] args) { 332 | String s = stampToDate("1502964348398"); 333 | System.out.println(s); 334 | } 335 | } 336 | 337 | 338 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/ICollectionUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import org.springframework.util.CollectionUtils; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.function.Function; 10 | import java.util.stream.Collectors; 11 | 12 | /** 13 | * @Description: 集合处理工具集 14 | * @Author: muggle 15 | * @Date: 2020/7/28 16 | **/ 17 | public class ICollectionUtils extends CollectionUtils { 18 | 19 | /** 20 | * 寻求优化空间 如自定义map 自定义list 21 | * @param args 22 | */ 23 | public static void main(String[] args) { 24 | Map map=new HashMap<>(); 25 | List list=new ArrayList<>(); 26 | List collect = map.values().stream().collect(Collectors.toList()); 27 | Map collect1 = list.stream().collect(Collectors.toMap(key -> key, value -> value, (k1, k2) -> k2)); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/INumberUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import org.springframework.util.NumberUtils; 4 | 5 | /** 6 | * @Description: 7 | * @Author: muggle 8 | * @Date: 2020/6/5 9 | **/ 10 | public class INumberUtils extends NumberUtils { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/IStringUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import org.springframework.util.StringUtils; 4 | 5 | /** 6 | * @Description: 7 | * @Author: muggle 8 | * @Date: 2020/6/5 9 | **/ 10 | public class IStringUtils extends StringUtils { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/JSONUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.TimeZone; 5 | 6 | import com.fasterxml.jackson.annotation.JsonInclude; 7 | import com.fasterxml.jackson.core.JsonProcessingException; 8 | import com.fasterxml.jackson.databind.ObjectMapper; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.util.StringUtils; 12 | 13 | /** 14 | * @Description: 15 | * @Author: muggle 16 | * @Date: 2020/8/27 17 | **/ 18 | public class JSONUtils { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(JSONUtils.class); 21 | 22 | private static final ObjectMapper MAPPER = new ObjectMapper(); 23 | 24 | private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; 25 | 26 | static { 27 | MAPPER.setTimeZone(TimeZone.getDefault()); 28 | MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); 29 | } 30 | 31 | /** 32 | * 将对象序列化为JSON字符串 33 | * 34 | * @param object 35 | * @return 36 | */ 37 | public static String toJacksonString(Object object) { 38 | if (object == null) { 39 | return null; 40 | } 41 | try { 42 | return MAPPER.writeValueAsString(object); 43 | } catch (JsonProcessingException e) { 44 | logger.warn("Parse object to json error: ", e); 45 | } 46 | return ""; 47 | } 48 | 49 | public static T paserJacsonObject(String src, Class clazz) { 50 | if (StringUtils.isEmpty(src) || clazz == null) { 51 | return null; 52 | } 53 | try { 54 | return String.class.equals(clazz) ? (T) src : MAPPER.readValue(src, clazz); 55 | } catch (Exception e) { 56 | logger.warn("Parse json to object error: ", e); 57 | } 58 | return null; 59 | } 60 | public static T paserJacsonObject(String src, Class clazz, SimpleDateFormat dateFormat) { 61 | if (StringUtils.isEmpty(src) || clazz == null) { 62 | return null; 63 | } 64 | try { 65 | ObjectMapper objectMapper = buildJacson(dateFormat); 66 | return String.class.equals(clazz) ? (T) src : objectMapper.readValue(src, clazz); 67 | } catch (Exception e) { 68 | logger.warn("Parse json to object error: ", e); 69 | } 70 | return null; 71 | } 72 | private static ObjectMapper buildJacson(SimpleDateFormat dateFormat){ 73 | ObjectMapper objectMapper = new ObjectMapper(); 74 | objectMapper.setTimeZone(TimeZone.getDefault()); 75 | objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 76 | objectMapper.setDateFormat(dateFormat); 77 | return objectMapper; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/JwtTokenUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import java.util.Arrays; 4 | import java.util.Date; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.UUID; 8 | 9 | import com.muggle.poseidon.base.exception.SimplePoseidonException; 10 | import com.muggle.poseidon.properties.SecurityMessageProperties; 11 | import io.jsonwebtoken.Claims; 12 | import io.jsonwebtoken.ExpiredJwtException; 13 | import io.jsonwebtoken.Jwts; 14 | import io.jsonwebtoken.SignatureAlgorithm; 15 | 16 | /** 17 | * @program: poseidon 18 | * @description: Token工具类 19 | * @author: muggle 20 | * @create: 2018-12-22 15:05 21 | **/ 22 | 23 | public class JwtTokenUtils { 24 | private static final long EXPIRATION = 3600000L; 25 | 26 | /** 27 | * 创建一个带过期时间的token experTime单位为小时 28 | * 29 | * @param map 30 | * @param credential 31 | * @param experTime 32 | * @return 33 | */ 34 | public static String createToken(Map map, String credential, Long experTime) { 35 | String compact = Jwts.builder().signWith(SignatureAlgorithm.HS512, credential) 36 | .setIssuer(SecurityMessageProperties.ISSUER) 37 | .setSubject(SecurityMessageProperties.SUBJECT) 38 | .setIssuedAt(new Date()) 39 | .setExpiration(new Date(System.currentTimeMillis() + experTime * EXPIRATION)) 40 | .setClaims(map) 41 | .compact(); 42 | return compact; 43 | } 44 | 45 | /** 46 | * 获得用户信息所使用的key 47 | * 48 | * @param token 49 | * @param credential 50 | * @return 51 | */ 52 | public static String getStoreKey(String token, String credential) { 53 | Claims body = Jwts.parser() 54 | .setSigningKey(credential) 55 | .parseClaimsJws(token) 56 | .getBody(); 57 | String key = body.get("key", String.class); 58 | return key; 59 | } 60 | 61 | /** 62 | * 获得版本号 63 | * 64 | * @param token 65 | * @param credential 66 | * @return 67 | */ 68 | public static String getRandom(String token, String credential) { 69 | try { 70 | Claims body = Jwts.parser() 71 | .setSigningKey(credential) 72 | .parseClaimsJws(token) 73 | .getBody(); 74 | String random = body.get(SecurityMessageProperties.RANDOM, String.class); 75 | return random; 76 | } catch (ExpiredJwtException e) { 77 | throw new SimplePoseidonException("用户登录过期,请重新登录"); 78 | } 79 | } 80 | 81 | /** 82 | * 根据key获得body 扩展性方法 83 | * 84 | * @param token 85 | * @param credential 86 | * @param key 87 | * @return 88 | */ 89 | public static Object getBody(String token, String credential, String key) { 90 | try { 91 | Claims claims = Jwts.parser() 92 | .setSigningKey(credential) 93 | .parseClaimsJws(token) 94 | .getBody(); 95 | return claims.get(key, Object.class); 96 | } catch (ExpiredJwtException e) { 97 | throw new SimplePoseidonException("用户登录过期,请重新登录"); 98 | } 99 | 100 | } 101 | 102 | public static String createToken(String storeKey, Map body, String credential, Long experTime) { 103 | UUID uuid = UUID.randomUUID(); 104 | body.put(SecurityMessageProperties.RANDOM, uuid.toString()); 105 | body.put("key", storeKey); 106 | return createToken(body, credential, experTime); 107 | } 108 | 109 | public static String createToken(Map body, String credential) { 110 | String compact = Jwts.builder().signWith(SignatureAlgorithm.HS512, credential) 111 | .setIssuedAt(new Date()) 112 | .setExpiration(new Date(System.currentTimeMillis() + 12 * EXPIRATION)) 113 | .setClaims(body) 114 | .compact(); 115 | return compact; 116 | } 117 | 118 | public static void main(String[] args) { 119 | // 120 | Map body = new HashMap<>(); 121 | body.put("username", "muggle"); 122 | body.put("version", System.currentTimeMillis()); 123 | body.put("roles", Arrays.asList("admin", "guest")); 124 | String test = createToken(body, "test"); 125 | System.out.println(test); 126 | } 127 | } 128 | /** 129 | * token使用思路 130 | * 1.token 的实效性 131 | * 创建token时带过期时间,每次先校验token是否过期 132 | * 2.token对应的用户信息存储 133 | * 存储在redis中 134 | * 3.登录再登出后作废token仍然生效。采用版本号 135 | * 4.token 的多人登录和单人登录要求灵活切换 136 | * 数据字典对应的系统设置 137 | * Redis信息多版本。 138 | */ -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/LocalDateUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | /** 4 | * @Description: 5 | * @Author: muggle 6 | * @Date: 2020/8/27 7 | **/ 8 | public class LocalDateUtils { 9 | // todo 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/NamedThreadFactory.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | import java.util.concurrent.atomic.AtomicLong; 5 | 6 | /** 7 | * @Description: 8 | * @Author: muggle 9 | * @Date: 2020/7/28 10 | **/ 11 | public class NamedThreadFactory implements ThreadFactory { 12 | private final String prefix; 13 | private final AtomicLong threadIds; 14 | 15 | NamedThreadFactory(String prefix) { 16 | this.prefix = prefix; 17 | this.threadIds = new AtomicLong(); 18 | } 19 | 20 | @Override 21 | public Thread newThread(Runnable r) { 22 | Thread t = new Thread(r, this.prefix + this.threadIds.incrementAndGet()); 23 | t.setDaemon(false); 24 | return t; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/PoseidonIdGenerator.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @program: poseidon-cloud-starter 7 | * @description: 发号器 8 | * @author: muggle 9 | * @create: 2019-11-25 10 | **/ 11 | 12 | public abstract class PoseidonIdGenerator { 13 | 14 | 15 | /** 16 | * 起始的时间戳 17 | */ 18 | private final static long START_STMP = 1480166465631L; 19 | 20 | /** 21 | * 每一部分占用的位数 22 | */ 23 | private final static long SEQUENCE_BIT = 12; //序列号占用的位数 24 | private final static long MACHINE_BIT = 5; //机器标识占用的位数 25 | private final static long DATACENTER_BIT = 5;//数据中心占用的位数 26 | 27 | /** 28 | * 每一部分的最大值 29 | */ 30 | private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT); 31 | private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); 32 | private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); 33 | 34 | /** 35 | * 每一部分向左的位移 36 | */ 37 | private final static long MACHINE_LEFT = SEQUENCE_BIT; 38 | private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; 39 | private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; 40 | 41 | private long datacenterId; //数据中心 42 | private long machineId; //机器标识 43 | private long sequence = 0L; //序列号 44 | private long lastStmp = -1L;//上一次时间戳 45 | 46 | /** 47 | * 获取一个流水单号 48 | * 49 | * @return 50 | */ 51 | public abstract String getNextSerialNumber(String perfix, String mark, int size); 52 | 53 | 54 | /** 55 | * 获取多个流水号 56 | * 57 | * @param perfix 58 | * @param mark 59 | * @param length 60 | * @return 61 | */ 62 | public abstract List getBatchSerialNumber(String perfix, String mark, int length, int size); 63 | 64 | // 65 | public PoseidonIdGenerator(long datacenterId, long machineId) { 66 | if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { 67 | throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); 68 | } 69 | if (machineId > MAX_MACHINE_NUM || machineId < 0) { 70 | throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); 71 | } 72 | this.datacenterId = datacenterId; 73 | this.machineId = machineId; 74 | } 75 | 76 | /** 77 | * 产生下一个ID 78 | * 79 | * @return 80 | */ 81 | public synchronized long simpleNextId() { 82 | long currStmp = getNewstmp(); 83 | if (currStmp < lastStmp) { 84 | throw new RuntimeException("Clock moved backwards. Refusing to generate id"); 85 | } 86 | 87 | if (currStmp == lastStmp) { 88 | //相同毫秒内,序列号自增 89 | sequence = (sequence + 1) & MAX_SEQUENCE; 90 | //同一毫秒的序列数已经达到最大 91 | if (sequence == 0L) { 92 | currStmp = getNextMill(); 93 | } 94 | } else { 95 | //不同毫秒内,序列号置为0 96 | sequence = 0L; 97 | } 98 | 99 | lastStmp = currStmp; 100 | 101 | return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分 102 | | datacenterId << DATACENTER_LEFT //数据中心部分 103 | | machineId << MACHINE_LEFT //机器标识部分 104 | | sequence; //序列号部分 105 | } 106 | 107 | private long getNextMill() { 108 | long mill = getNewstmp(); 109 | while (mill <= lastStmp) { 110 | mill = getNewstmp(); 111 | } 112 | return mill; 113 | } 114 | 115 | private long getNewstmp() { 116 | return System.currentTimeMillis(); 117 | } 118 | 119 | public abstract long nextId(); 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/RSACoder.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | 4 | /** 5 | * @program: security-test 6 | * @description: 非对称加密算法 7 | * @author: muggle 8 | * @create: 2019-04-20 9 | **/ 10 | 11 | 12 | import jakarta.crypto.Cipher; 13 | import java.security.Key; 14 | import java.security.KeyFactory; 15 | import java.security.KeyPair; 16 | import java.security.KeyPairGenerator; 17 | import java.security.PrivateKey; 18 | import java.security.PublicKey; 19 | import java.security.interfaces.RSAPrivateKey; 20 | import java.security.interfaces.RSAPublicKey; 21 | import java.security.spec.PKCS8EncodedKeySpec; 22 | import java.security.spec.X509EncodedKeySpec; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | /** 27 | * 非对称加密算法RSA算法组件 28 | * 非对称算法一般是用来传送对称加密算法的密钥来使用的,相对于DH算法,RSA算法只需要一方构造密钥,不需要 29 | * 大费周章的构造各自本地的密钥对了。DH算法只能算法非对称算法的底层实现。而RSA算法算法实现起来较为简单 30 | * 31 | * @author kongqz 32 | */ 33 | public class RSACoder { 34 | //非对称密钥算法 35 | public static final String KEY_ALGORITHM = "RSA"; 36 | 37 | 38 | /** 39 | * 密钥长度,DH算法的默认密钥长度是1024 40 | * 密钥长度必须是64的倍数,在512到65536位之间 41 | */ 42 | private static final int KEY_SIZE = 512; 43 | //公钥 44 | private static final String PUBLIC_KEY = "RSAPublicKey"; 45 | 46 | //私钥 47 | private static final String PRIVATE_KEY = "RSAPrivateKey"; 48 | 49 | /** 50 | * 初始化密钥对 51 | * 52 | * @return Map 甲方密钥的Map 53 | */ 54 | public static Map initKey() throws Exception { 55 | //实例化密钥生成器 56 | KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM); 57 | //初始化密钥生成器 58 | keyPairGenerator.initialize(KEY_SIZE); 59 | //生成密钥对 60 | KeyPair keyPair = keyPairGenerator.generateKeyPair(); 61 | //甲方公钥 62 | RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); 63 | //甲方私钥 64 | RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); 65 | //将密钥存储在map中 66 | Map keyMap = new HashMap(); 67 | keyMap.put(PUBLIC_KEY, publicKey); 68 | keyMap.put(PRIVATE_KEY, privateKey); 69 | return keyMap; 70 | 71 | } 72 | 73 | 74 | /** 75 | * 私钥加密 76 | * 77 | * @param data 待加密数据 78 | * @param key 密钥 79 | * @return byte[] 加密数据 80 | */ 81 | public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception { 82 | 83 | //取得私钥 84 | PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key); 85 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); 86 | //生成私钥 87 | PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); 88 | //数据加密 89 | Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); 90 | cipher.init(Cipher.ENCRYPT_MODE, privateKey); 91 | return cipher.doFinal(data); 92 | } 93 | 94 | /** 95 | * 公钥加密 96 | * 97 | * @param data 待加密数据 98 | * @param key 密钥 99 | * @return byte[] 加密数据 100 | */ 101 | public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception { 102 | 103 | //实例化密钥工厂 104 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); 105 | //初始化公钥 106 | //密钥材料转换 107 | X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key); 108 | //产生公钥 109 | PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); 110 | 111 | //数据加密 112 | Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); 113 | cipher.init(Cipher.ENCRYPT_MODE, pubKey); 114 | return cipher.doFinal(data); 115 | } 116 | 117 | /** 118 | * 私钥解密 119 | * 120 | * @param data 待解密数据 121 | * @param key 密钥 122 | * @return byte[] 解密数据 123 | */ 124 | public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception { 125 | //取得私钥 126 | PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key); 127 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); 128 | //生成私钥 129 | PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); 130 | //数据解密 131 | Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); 132 | cipher.init(Cipher.DECRYPT_MODE, privateKey); 133 | return cipher.doFinal(data); 134 | } 135 | 136 | /** 137 | * 公钥解密 138 | * 139 | * @param data 待解密数据 140 | * @param key 密钥 141 | * @return byte[] 解密数据 142 | */ 143 | public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception { 144 | 145 | //实例化密钥工厂 146 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); 147 | //初始化公钥 148 | //密钥材料转换 149 | X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key); 150 | //产生公钥 151 | PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); 152 | //数据解密 153 | Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); 154 | cipher.init(Cipher.DECRYPT_MODE, pubKey); 155 | return cipher.doFinal(data); 156 | } 157 | 158 | /** 159 | * 取得私钥 160 | * 161 | * @param keyMap 密钥map 162 | * @return byte[] 私钥 163 | */ 164 | public static byte[] getPrivateKey(Map keyMap) { 165 | Key key = (Key) keyMap.get(PRIVATE_KEY); 166 | return key.getEncoded(); 167 | } 168 | 169 | /** 170 | * 取得公钥 171 | * 172 | * @param keyMap 密钥map 173 | * @return byte[] 公钥 174 | */ 175 | public static byte[] getPublicKey(Map keyMap) throws Exception { 176 | Key key = (Key) keyMap.get(PUBLIC_KEY); 177 | return key.getEncoded(); 178 | } 179 | 180 | public static void main(String[] args) throws Exception { 181 | //初始化密钥 182 | //生成密钥对 183 | Map keyMap = RSACoder.initKey(); 184 | //公钥 185 | byte[] publicKey = RSACoder.getPublicKey(keyMap); 186 | 187 | //私钥 188 | byte[] privateKey = RSACoder.getPrivateKey(keyMap); 189 | 190 | String str = "RSA密码交换算法"; 191 | 192 | //甲方进行数据的加密 193 | byte[] code1 = RSACoder.encryptByPrivateKey(str.getBytes(), privateKey); 194 | 195 | //乙方进行数据的解密 196 | byte[] decode1 = RSACoder.decryptByPublicKey(code1, publicKey); 197 | 198 | Map ss = RSACoder.initKey(); 199 | 200 | //公钥 201 | byte[] a = RSACoder.getPublicKey(keyMap); 202 | 203 | //私钥 204 | byte[] b = RSACoder.getPrivateKey(keyMap); 205 | 206 | byte[] dexx = RSACoder.decryptByPublicKey(code1, a); 207 | 208 | System.out.println(new String(dexx)); 209 | 210 | /* str = "乙方向甲方发送数据RSA算法"; 211 | 212 | //乙方使用公钥对数据进行加密 213 | byte[] code2 = RSACoder.encryptByPublicKey(str.getBytes(), publicKey); 214 | 215 | //甲方使用私钥对数据进行解密 216 | byte[] decode2 = RSACoder.decryptByPrivateKey(code2, privateKey); 217 | 218 | System.out.println("甲方解密后的数据:" + new String(decode2));*/ 219 | } 220 | 221 | } -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/RequestUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | 5 | /** 6 | * @program: poseidon-cloud-starter 7 | * @description: 8 | * @author: muggle 9 | * @create: 2019-11-29 10 | **/ 11 | 12 | public class RequestUtils { 13 | 14 | /** 15 | * 获取真实的IP地址 16 | * 17 | * @param request 请求 18 | * @return 19 | */ 20 | public static String getIP(HttpServletRequest request) { 21 | String ip = request.getHeader("x-forwarded-for"); 22 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 23 | ip = request.getHeader("Proxy-Client-IP"); 24 | } 25 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 26 | ip = request.getHeader("WL-Proxy-Client-IP"); 27 | } 28 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 29 | ip = request.getHeader("HTTP_CLIENT_IP"); 30 | } 31 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 32 | ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 33 | } 34 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 35 | ip = request.getRemoteAddr(); 36 | } 37 | return ip; 38 | } 39 | // todo 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/SHA256Utils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.nio.charset.StandardCharsets; 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | 8 | /** 9 | * @Description: 10 | * @Author: muggle 11 | * @Date: 2020/8/28 12 | **/ 13 | public class SHA256Utils { 14 | 15 | public final static String SHA256="SHA-256"; 16 | 17 | private SHA256Utils() { 18 | throw new UnsupportedOperationException(); 19 | } 20 | 21 | public static String getSHA256StrJava(String timeStamp, String token, String passId, String collection) { 22 | return getSHA256StrJava(timeStamp + token + passId + collection); 23 | } 24 | 25 | public static String getSHA256StrJava(String str) { 26 | String encodeStr = ""; 27 | 28 | try { 29 | MessageDigest messageDigest = MessageDigest.getInstance(SHA256); 30 | messageDigest.update(str.getBytes(StandardCharsets.UTF_8)); 31 | encodeStr = byte2Hex(messageDigest.digest()); 32 | } catch (NoSuchAlgorithmException var4) { 33 | var4.printStackTrace(); 34 | } 35 | 36 | return encodeStr; 37 | } 38 | 39 | public static String getSHA512Encrypt(String str) { 40 | String encodeStr = ""; 41 | 42 | try { 43 | MessageDigest messageDigest = MessageDigest.getInstance(SHA256); 44 | messageDigest.update(str.getBytes(StandardCharsets.UTF_8)); 45 | encodeStr = byte2Hex(messageDigest.digest()); 46 | } catch (NoSuchAlgorithmException var4) { 47 | var4.printStackTrace(); 48 | } 49 | 50 | return encodeStr; 51 | } 52 | 53 | private static String byte2Hex(byte[] bytes) { 54 | StringBuffer stringBuffer = new StringBuffer(); 55 | String temp = null; 56 | 57 | for(int i = 0; i < bytes.length; ++i) { 58 | temp = Integer.toHexString(bytes[i] & 255); 59 | if (temp.length() == 1) { 60 | stringBuffer.append("0"); 61 | } 62 | 63 | stringBuffer.append(temp); 64 | } 65 | 66 | return stringBuffer.toString(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/SimpleLocker.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import java.util.Iterator; 4 | import java.util.Map; 5 | import java.util.Set; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.concurrent.locks.Condition; 9 | 10 | import com.muggle.poseidon.base.DistributedLocker; 11 | import com.muggle.poseidon.base.exception.BasePoseidonCheckException; 12 | import com.muggle.poseidon.base.exception.SimplePoseidonCheckException; 13 | 14 | 15 | public class SimpleLocker implements DistributedLocker { 16 | 17 | 18 | @Override 19 | public boolean tryLock(String key, long expertime) throws InterruptedException { 20 | return false; 21 | } 22 | 23 | @Override 24 | public void lock(String key) { 25 | 26 | } 27 | 28 | @Override 29 | public void unlock(String key) { 30 | 31 | } 32 | 33 | @Override 34 | public void lock() { 35 | 36 | } 37 | 38 | @Override 39 | public void lockInterruptibly() throws InterruptedException { 40 | 41 | } 42 | 43 | @Override 44 | public boolean tryLock() { 45 | return false; 46 | } 47 | 48 | @Override 49 | public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { 50 | return false; 51 | } 52 | 53 | @Override 54 | public void unlock() { 55 | 56 | } 57 | 58 | @Override 59 | public Condition newCondition() { 60 | return null; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/ThreadPoolUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.concurrent.*; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | 9 | /** 10 | * @Description: 11 | * @Author: muggle 12 | * @Date: 2020/7/28 13 | **/ 14 | public class ThreadPoolUtils { 15 | private static final Logger log = LoggerFactory.getLogger(ThreadPoolUtils.class); 16 | private static final String DEFAULT_EXEUTOR_NAME = "POSEIDON-DEFAULT-POOL-"; 17 | private static final int DEFAULT_POOL_SIEZ = 100; 18 | private static final int DEFAULT_MAX_POOL_SIZE = 200; 19 | private static final int DEFAULT_QUEUE_SIZE = 1000; 20 | private static final long DEFAULT_KEEP_ALIVE = 5L; 21 | 22 | private ThreadPoolUtils() { 23 | 24 | } 25 | 26 | // 1. 线程池工厂 thraedfactory 作用 2. 四种handler 27 | /** 28 | * 1. AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常。(默认这种) 29 | * 2. DiscardPolicy:也是丢弃任务,但是不抛出异常 30 | * 3. DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) 31 | * 4. CallerRunsPolicy:由调用线程处理该任务 32 | * */ 33 | public static ThreadPoolExecutor buildDefaultPool() { 34 | ThreadPoolExecutor executor = new ThreadPoolExecutor(DEFAULT_POOL_SIEZ, DEFAULT_MAX_POOL_SIZE, DEFAULT_KEEP_ALIVE, TimeUnit.MINUTES, 35 | new LinkedBlockingQueue(DEFAULT_QUEUE_SIZE), new NamedThreadFactory(DEFAULT_EXEUTOR_NAME), 36 | new ThreadPoolExecutor.CallerRunsPolicy()); 37 | return executor; 38 | } 39 | 40 | public static ThreadPoolExecutor buildThreadPool(String poolName){ 41 | ThreadPoolExecutor executor = new ThreadPoolExecutor(DEFAULT_POOL_SIEZ, DEFAULT_MAX_POOL_SIZE, DEFAULT_KEEP_ALIVE, TimeUnit.MINUTES, 42 | new LinkedBlockingQueue(DEFAULT_QUEUE_SIZE), new NamedThreadFactory(poolName), 43 | new ThreadPoolExecutor.CallerRunsPolicy()); 44 | return executor; 45 | } 46 | 47 | public static ThreadPoolExecutor buildThreadPool(String poolName,RejectedExecutionHandler handler){ 48 | ThreadPoolExecutor executor = new ThreadPoolExecutor(DEFAULT_POOL_SIEZ, DEFAULT_MAX_POOL_SIZE, DEFAULT_KEEP_ALIVE, TimeUnit.MINUTES, 49 | new LinkedBlockingQueue(DEFAULT_QUEUE_SIZE), new NamedThreadFactory(poolName), handler); 50 | return executor; 51 | } 52 | 53 | public static void main(String[] args) { 54 | ThreadPoolExecutor threadPoolExecutor = ThreadPoolUtils.buildDefaultPool(); 55 | threadPoolExecutor.execute(()->{ 56 | System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>."); 57 | }); 58 | threadPoolExecutor.shutdown(); 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/UserInfoUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | import com.muggle.poseidon.base.exception.BasePoseidonCheckException; 4 | import com.muggle.poseidon.base.exception.SimplePoseidonCheckException; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | 9 | /** 10 | * @program: poseidon-cloud-starter 11 | * @description: 获取用户信息 12 | * @author: muggle 13 | * @create: 2019-11-06 14 | **/ 15 | 16 | public class UserInfoUtils { 17 | 18 | public static UserDetails getUserInfo() throws BasePoseidonCheckException { 19 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 20 | if (authentication == null || authentication.getDetails() == null || authentication.getPrincipal().equals("anonymousUser")) { 21 | return null; 22 | } 23 | if (!(authentication.getDetails() instanceof UserDetails)) { 24 | throw new SimplePoseidonCheckException("用户登陆信息异常"); 25 | } 26 | return (UserDetails) authentication.getDetails(); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/com/muggle/poseidon/util/ZipUtils.java: -------------------------------------------------------------------------------- 1 | package com.muggle.poseidon.util; 2 | 3 | 4 | import java.io.*; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.zip.ZipEntry; 8 | import java.util.zip.ZipInputStream; 9 | import java.util.zip.ZipOutputStream; 10 | 11 | /** 12 | * 压缩工具 13 | * @author nick 14 | */ 15 | public class ZipUtils { 16 | 17 | public static class ZipItem { 18 | /**文件名,包含目录形式:用/隔开,例如a/b/c.txt*/ 19 | public String fileName; 20 | /**文件对应的输入流*/ 21 | public InputStream in; 22 | } 23 | 24 | public static class Iterator { 25 | private ZipInputStream zin; 26 | private Iterator(ZipInputStream zin) { 27 | this.zin = zin; 28 | } 29 | /** 30 | * 获得下一个ZipItem,当返回null则表示读取完。【重要】ZipItem中的输入流in必须在本次读取完。 31 | * @return 32 | * @throws IOException 33 | */ 34 | public ZipItem next() throws IOException { 35 | ZipEntry ze = zin.getNextEntry(); 36 | if(ze == null) { 37 | return null; 38 | } 39 | ZipItem zipItem = new ZipItem(); 40 | zipItem.fileName = ze.getName(); 41 | zipItem.in = zin; 42 | return zipItem; 43 | } 44 | } 45 | 46 | /** 47 | * 压缩文件 48 | * @param zipItems 49 | * @param out 不会自动close 50 | */ 51 | public static void zip(List zipItems, OutputStream out) throws IOException { 52 | ZipOutputStream zipOut = new ZipOutputStream(out); 53 | if(zipItems != null) { 54 | for(ZipItem zipItem : zipItems) { 55 | zipOut.putNextEntry(new ZipEntry(zipItem.fileName)); 56 | int len = -1; 57 | byte[] buff = new byte[4096]; 58 | while ((len = zipItem.in.read(buff)) != -1) { 59 | zipOut.write(buff, 0, len); 60 | } 61 | zipItem.in.close(); 62 | } 63 | } 64 | zipOut.close(); 65 | } 66 | 67 | /** 68 | * 解压文件,节省内存方式 69 | * @param in 不会自动close 70 | * @return 71 | */ 72 | public static Iterator unzip(InputStream in) throws Exception { 73 | ZipInputStream zin = new ZipInputStream(in); 74 | return new Iterator(zin); 75 | } 76 | 77 | /** 78 | * 解压文件,会将文件解压到内存中,该方式会占用较多内存 79 | * @param in 80 | * @return 81 | * @throws Exception 82 | */ 83 | public static List unzipAll(InputStream in) throws Exception { 84 | Iterator it = unzip(in); 85 | ZipItem zipItem = null; 86 | List zipItems = new ArrayList<>(); 87 | while((zipItem = it.next()) != null) { 88 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 89 | // IOUtils.copy(zipItem.in, out); 90 | zipItem.in = new ByteArrayInputStream(out.toByteArray()); 91 | zipItems.add(zipItem); 92 | } 93 | return zipItems; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.muggle.poseidon.auto.ExpansibilityConfig,\ 3 | com.muggle.poseidon.auto.SecurityAutoConfig,\ 4 | com.muggle.poseidon.handler.web.WebUrlHandler,\ 5 | com.muggle.poseidon.handler.web.WebResultHandler -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | /$$ /$$ 2 | |__/ | $$ 3 | /$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$ /$$ /$$$$$$$ /$$$$$$ /$$$$$$$ 4 | /$$__ $$ /$$__ $$ /$$_____/ /$$__ $$| $$ /$$__ $$ /$$__ $$| $$__ $$ 5 | | $$ \ $$| $$ \ $$| $$$$$$ | $$$$$$$$| $$| $$ | $$| $$ \ $$| $$ \ $$ 6 | | $$ | $$| $$ | $$ \____ $$| $$_____/| $$| $$ | $$| $$ | $$| $$ | $$ 7 | | $$$$$$$/| $$$$$$/ /$$$$$$$/| $$$$$$$| $$| $$$$$$$| $$$$$$/| $$ | $$ 8 | | $$____/ \______/ |_______/ \_______/|__/ \_______/ \______/ |__/ |__/ 9 | | $$ 10 | | $$ 11 | |__/ -------------------------------------------------------------------------------- /src/main/resources/poseidon-logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | %white(POSEIDON----) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %cyan(%logger) - %msg%n 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ${log_dir}/%d{yyyy-MM-dd}-info.log 29 | 30 | ${maxHistory} 31 | 32 | 33 | 34 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n 35 | 36 | 37 | 38 | INFO 39 | ACCEPT 40 | DENY 41 | 42 | 43 | 44 | 45 | 46 | 47 | ${log_dir}/%d{yyyy-MM-dd}-error.log 48 | 49 | ${maxHistory} 50 | 51 | 52 | 53 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n 54 | 55 | 56 | 57 | ERROR 58 | ACCEPT 59 | DENY 60 | 61 | 62 | 63 | 64 | 65 | ${log_dir}/%d{yyyy-MM-dd}-debug.log 66 | 67 | ${maxHistory} 68 | 69 | 70 | 71 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n 72 | 73 | 74 | 75 | DEBUG 76 | ACCEPT 77 | DENY 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | --------------------------------------------------------------------------------