├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── pom.xml ├── spring-boot-shiro-autoconfigure ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── millinch │ │ └── spring │ │ └── boot │ │ └── autoconfigure │ │ └── shiro │ │ ├── FormSignInFilter.java │ │ ├── JdbcPermissionDefinitionsLoader.java │ │ ├── RetryLimitHashedCredentialsMatcher.java │ │ ├── ShiroAutoConfiguration.java │ │ ├── ShiroConfiguration.java │ │ ├── ShiroCookieProperties.java │ │ ├── ShiroFilterCustomizer.java │ │ ├── ShiroJdbcRealmProperties.java │ │ ├── ShiroProperties.java │ │ ├── ShiroSessionProperties.java │ │ ├── ShiroSignInProperties.java │ │ ├── ShiroWebMvcConfigurerAdapter.java │ │ └── annotation │ │ ├── EnableShiroWebSupport.java │ │ ├── SessionUser.java │ │ └── SessionUserArgumentResolver.java │ └── resources │ └── META-INF │ └── spring.factories ├── spring-boot-shiro-sample ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── millinch │ │ └── springboot │ │ └── shiro │ │ └── sample │ │ ├── SampleApplication.java │ │ ├── domain │ │ └── User.java │ │ ├── realm │ │ └── FirstRealm.java │ │ └── web │ │ ├── MyFilter.java │ │ └── SampleApi.java │ └── resources │ ├── application.yml │ ├── data-mysql.sql │ ├── schema-mysql.sql │ └── static │ ├── index.html │ ├── login.html │ └── unauthorized.html └── spring-boot-shiro-starter ├── pom.xml └── src └── main └── resources └── META-INF └── spring.provides /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.war 3 | *.ear 4 | *.log 5 | *.bak 6 | **/.checkstyle 7 | rebel.xml 8 | 9 | .classpath 10 | !/.project 11 | .project 12 | .settings 13 | .DS_Store 14 | 15 | classes/ 16 | target 17 | test-output/ 18 | build 19 | bin/ 20 | overlays/ 21 | pom.xml.versionsBackup 22 | temp/ 23 | 24 | 25 | # IntelliJ IDEA 26 | .idea 27 | *.iml 28 | *.ipr 29 | *.iws 30 | .idea/ 31 | build/ 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | script: mvn clean install -DskipTests=true 3 | -------------------------------------------------------------------------------- /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, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "{}" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright 2016 John Zhang 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Apache Shiro integration with Spring Boot 2 | ========================================= 3 | [![Build Status](https://travis-ci.org/johntostring/spring-boot-shiro.svg?branch=master)](https://travis-ci.org/johntostring/spring-boot-shiro) 4 | 5 | **Github:**[https://github.com/johntostring/spring-boot-shiro](https://github.com/johntostring/spring-boot-shiro) 6 | 7 | ## Usage 8 | 9 | 首先安装到本地仓库。 10 | 11 | 分支`v2.0`下为`2.0.0`版本,主要将Spring Boot升级到2.0.4.RELEASE,Shiro 升级到 1.4.0 12 | 13 | `mvn clean install` 14 | 15 | ### pom.xml 16 | 17 | ```xml 18 | 19 | com.millinch 20 | spring-boot-shiro-starter 21 | 1.0.0 22 | 23 | ``` 24 | 25 | ### QuickStart 26 | 官方推荐采用YAML的方式配置,这里我也建议使用YAML来配置,一方面是因为在配置filter-chain-definitions时,通常我们是希望保证顺序的,使用properties的方式无法保证顺序(继承自Hashtable)。 27 | 后面将会列出配置的默认约定,按照你的需要,你只需要配置你想要修改的项就可以了。 28 | 关于SpringBoot详细使用方式,你可以查看[官方文档](http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#common-application-properties),或者通过[Github开源地址](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples)查看一些示例项目。 29 | 30 | 首先,默认提供JdbcRealm 配置,不能满足需求的情况下再配置自定义Realm: 31 | ```yaml 32 | shiro: 33 | realm-class: #默认空,指定类全名com.your.company.YourRealm 34 | ``` 35 | 36 | **1. 配置DataSource** 37 | 38 | 以MySQL为例,添加好JDBC驱动依赖后,这是 Spring Boot 默认提供的方式配置DataSource: 39 | ```yaml 40 | spring: 41 | datasource: 42 | url: jdbc:mysql://localhost:3306/yourdbname 43 | username: #you know 44 | password: #you know 45 | driver-class-name: com.mysql.jdbc.Driver 46 | ``` 47 | 48 | **2. 开启JdbcRealm** 49 | 50 | 首先设shiro.realm.jdbc.enabled为true,否则就会查找自定义的realm-class,最后配置好SQL查询语句。 51 | 52 | 默认配置及描述: 53 | ```yaml 54 | shiro: 55 | realm: 56 | jdbc: 57 | enabled: true 58 | authentication-query: SELECT password FROM sys_user where user_name = ? #根据用户名获取密码 59 | salt: no_salt #可选值:no_salt, crypt(源码中未实现), column(上面这个SQL中第二列中获取salt), external(需继承JdbcRealm重写getSaltForUser()方法) 60 | user-roles-query: SELECT r.code 61 | FROM sys_user_role ur 62 | LEFT JOIN sys_role r ON r.id = ur.role_id 63 | LEFT JOIN sys_user u ON ur.user_id = u.id AND u.user_name = ? #根据用户名获取角色 64 | permissions-query: SELECT re.permission 65 | FROM sys_role_resource rr 66 | LEFT JOIn sys_resource re ON rr.resource_id = re.id 67 | LEFT JOIN sys_role r ON rr.role_id = r.id 68 | WHERE r.code = ? #根据角色获取权限 69 | login-url: /login #登录入口URL 70 | success-url: /index #登录成功跳转URL 71 | unauthorized-url: /unauthorized #当访问未授权页面时跳转至该URL,将为filter chain中的每个AuthorizationFilter设置跳转URL(如果目标没有指定) 72 | sign-in: 73 | user-param: username #用户名参数名称 74 | password-param: password #密码参数名称 75 | remember-me-param: rememberMe #记住我参数名称 76 | hash-iterations: 1 #加密迭代次数,强制设为至少1次(即使设置0或负数) 77 | hash-algorithm-name: MD5 #加密算法名称,如:MD2/SHA-1/SHA-256/SHA-384/SHA-512 78 | filters: 79 | demo: your.packagepath.to.DemoFilter 80 | filter-chain-definitions: #默认为空,一般如下配置 81 | /login: authc 82 | /logout: logout 83 | /favicon.ico: anon 84 | /index: anon 85 | /assets/**: anon 86 | /**: authc 87 | ``` 88 | 89 | **注意:在升级到Spring Boot 2+时我才发现`filter-chain-definitions`的配置中,key如果包含特殊字符会被强行去除掉,需要如下写法才可以:** 90 | 91 | ```yaml 92 | filter-chain-definitions: 93 | '[/sayHi]': authc, demo 94 | ``` 95 | 96 | 参考:https://stackoverflow.com/questions/34070987/escaping-a-dot-in-a-map-key-in-yaml-in-spring-boot/42285949 97 | 98 | #### Cookie 99 | 100 | ```properties 101 | shiro.cookie.cipher-key= #对称加密时使用(默认),为空则Shrio自动提供一个 102 | shiro.cookie.decryption-cipher-key= #非对称加密时必需 103 | shiro.cookie.encryption-cipher-key= #非对称加密时必需 104 | shiro.cookie.name=rememberMe 105 | shiro.cookie.http-only=true #建议开启 106 | shiro.cookie.max-age=31536000 #1年 107 | shiro.cookie.secure=false #安全传输cookie 108 | shiro.cookie.version=-1 109 | ``` 110 | 111 | #### Session 112 | ```properties 113 | shiro.session.active-sessions-cache-name=shiro-acciveSessionCache 114 | shiro.session.delete-invalid-sessions=true 115 | shiro.session.global-session-timeout=36000 #Session超时时长,默认1小时 116 | shiro.session.id-generator=org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator #可指定自定义的实现 117 | shiro.session.validation-interval=36000 #每隔1小时验证Session 118 | shiro.session.validation-scheduler-enabled=true #是否开启定时验证Session 119 | ``` 120 | 121 | **关于SessionValidationScheduler** 122 | 123 | 默认配置ExecutorServiceSessionValidationScheduler,这是一个JDK的并发包API的实现。 124 | 若在classpath发现Quartz,则自动使用QuartzSessionValidationScheduler。即添加dependency: 125 | ```xml 126 | 127 | org.apache.shiro 128 | shiro-quartz 129 | ${shiro.version} 130 | 131 | ``` 132 | 133 | 最后,你也可以配置一个自定义的`SessionValidationScheduler`实现: 134 | ```java 135 | @Bean(name = "sessionValidationScheduler") 136 | public SessionValidationScheduler yourSessionValidationScheduler() { 137 | return new YourSessionValidationScheduler(); 138 | } 139 | ``` 140 | 141 | #### Cache 142 | ```properties 143 | shiro.ehcache.cache-manager-config-file=classpath:org/apache/shiro/cache/ehcache/ehcache.xml #开启Ehcache时可指定 144 | ``` 145 | 146 | **CacheManager** 147 | 148 | 默认配置`MemoryConstrainedCacheManager`,不建议用于生产。 149 | 若在classpath发现`org.apache.shiro.cache.ehcache.EhCacheManager`,则自动使用EhCacheManager。即添加dependency: 150 | ```xml 151 | 152 | org.apache.shiro 153 | shiro-ehcache 154 | ${shiro.version} 155 | 156 | ``` 157 | 同样地,CacheManager也可以配置自定义的实现,比如Redis等分布式缓存的实现,来覆盖默认配置,其他的如Realm、CredentialsMatcher、SessionDAO同样可配置Bean来覆盖默认配置。 158 | 159 | **最后,会继续完善的,Demo也会抽时间做一个。有不足的地方请及时指出。** 160 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.millinch 7 | spring-boot-shiro 8 | 1.0.0-SNAPSHOT 9 | pom 10 | 11 | Millinch :: Spring Boot Shiro 12 | Apache Shiro integration with Spring Boot. 13 | 14 | 15 | spring-boot-shiro-autoconfigure 16 | spring-boot-shiro-starter 17 | 18 | 19 | 20 | UTF-8 21 | 1.7 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.millinch 7 | spring-boot-shiro-autoconfigure 8 | 1.0.0 9 | 10 | 11 | UTF-8 12 | 1.7 13 | 1.2.4 14 | 1.3.3.RELEASE 15 | 16 | 17 | 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-dependencies 22 | ${spring-boot.version} 23 | pom 24 | import 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.apache.shiro 32 | shiro-core 33 | ${shiro.version} 34 | 35 | 36 | org.apache.shiro 37 | shiro-spring 38 | ${shiro.version} 39 | 40 | 41 | org.apache.shiro 42 | shiro-web 43 | ${shiro.version} 44 | 45 | 46 | org.apache.shiro 47 | shiro-ehcache 48 | ${shiro.version} 49 | provided 50 | 51 | 52 | org.apache.shiro 53 | shiro-quartz 54 | ${shiro.version} 55 | provided 56 | 57 | 58 | javax.servlet 59 | servlet-api 60 | 2.5 61 | provided 62 | 63 | 64 | 65 | org.springframework.boot 66 | spring-boot 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-autoconfigure 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-starter-web 75 | 76 | 77 | 78 | org.springframework.boot 79 | spring-boot-configuration-processor 80 | true 81 | 82 | 83 | junit 84 | junit 85 | test 86 | 87 | 88 | 89 | 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-compiler-plugin 94 | 95 | ${java.version} 96 | ${java.version} 97 | ${java.version} 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/FormSignInFilter.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import org.apache.shiro.authc.AuthenticationException; 4 | import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; 5 | 6 | import javax.servlet.ServletRequest; 7 | import javax.servlet.ServletResponse; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | import java.io.PrintWriter; 12 | 13 | /** 14 | * This guy is lazy, nothing left. 15 | * 16 | * @author 张劲航 17 | */ 18 | public class FormSignInFilter extends FormAuthenticationFilter { 19 | 20 | @Override 21 | protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) { 22 | request.setAttribute(getFailureKeyAttribute(), ae); 23 | } 24 | 25 | @Override 26 | protected void redirectToLogin(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException { 27 | try { 28 | HttpServletRequest request = (HttpServletRequest) servletRequest; 29 | HttpServletResponse response = (HttpServletResponse) servletResponse; 30 | String contentType = request.getContentType(); 31 | if (contentType != null && contentType.contains("application/json")) { 32 | response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 33 | response.setCharacterEncoding("UTF-8"); 34 | PrintWriter writer = response.getWriter(); 35 | writer.print("{\"error\":\"登录超时,请重新登录。\"}"); 36 | } else { 37 | super.redirectToLogin(servletRequest, servletResponse); 38 | } 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | super.redirectToLogin(servletRequest, servletResponse); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/JdbcPermissionDefinitionsLoader.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import org.apache.shiro.util.JdbcUtils; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.factory.FactoryBean; 7 | 8 | import javax.sql.DataSource; 9 | import java.sql.Connection; 10 | import java.sql.PreparedStatement; 11 | import java.sql.ResultSet; 12 | import java.util.LinkedHashMap; 13 | import java.util.Map; 14 | 15 | /** 16 | * Load filter chain definitions vi JDBC query. 17 | * 18 | * @author John Zhang 19 | */ 20 | public class JdbcPermissionDefinitionsLoader implements FactoryBean> { 21 | 22 | private static final Logger LOGGER = LoggerFactory.getLogger(JdbcPermissionDefinitionsLoader.class); 23 | 24 | public static final String PERMISSION_STRING = "perms[\"%s\"]"; 25 | 26 | private DataSource dataSource; 27 | 28 | private String sql; 29 | 30 | public JdbcPermissionDefinitionsLoader(DataSource dataSource) { 31 | this.dataSource = dataSource; 32 | } 33 | 34 | @Override 35 | public Map getObject() throws Exception { 36 | Connection connection = dataSource.getConnection(); 37 | PreparedStatement ps = connection.prepareStatement(sql); 38 | ResultSet rs = null; 39 | Map filters = new LinkedHashMap<>(); 40 | 41 | try { 42 | rs = ps.executeQuery(); 43 | 44 | while (rs.next()) { 45 | 46 | String url = rs.getString(1); 47 | String permission = rs.getString(2); 48 | 49 | filters.put(url, String.format(PERMISSION_STRING, permission)); 50 | LOGGER.debug("Load filter chain via JDBC: {} -> {}", url, permission); 51 | } 52 | } finally { 53 | JdbcUtils.closeResultSet(rs); 54 | } 55 | return filters; 56 | } 57 | 58 | public void setDataSource(DataSource dataSource) { 59 | this.dataSource = dataSource; 60 | } 61 | 62 | public void setSql(String sql) { 63 | this.sql = sql; 64 | } 65 | 66 | @Override 67 | public Class getObjectType() { 68 | return this.getClass(); 69 | } 70 | 71 | @Override 72 | public boolean isSingleton() { 73 | return false; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/RetryLimitHashedCredentialsMatcher.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import org.apache.shiro.authc.AuthenticationInfo; 4 | import org.apache.shiro.authc.AuthenticationToken; 5 | import org.apache.shiro.authc.ExcessiveAttemptsException; 6 | import org.apache.shiro.authc.IncorrectCredentialsException; 7 | import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 8 | import org.apache.shiro.cache.Cache; 9 | import org.apache.shiro.cache.CacheManager; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | 15 | public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher { 16 | 17 | private static final Logger LOGGER = LoggerFactory.getLogger(RetryLimitHashedCredentialsMatcher.class); 18 | 19 | private Cache passwordRetryCache; 20 | private int retryMax = 5; 21 | 22 | public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) { 23 | passwordRetryCache = cacheManager.getCache("passwordRetryCache"); 24 | } 25 | 26 | @Override 27 | public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws ExcessiveAttemptsException { 28 | String username = (String)token.getPrincipal(); 29 | AtomicInteger retryCount = passwordRetryCache.get(username); 30 | 31 | if(retryCount == null) { 32 | retryCount = new AtomicInteger(0); 33 | passwordRetryCache.put(username, retryCount); 34 | } 35 | if(retryCount.incrementAndGet() > retryMax) { 36 | throw new ExcessiveAttemptsException("您已连续错误达" + retryMax + "次!请10分钟后再试"); 37 | } 38 | 39 | boolean matches = super.doCredentialsMatch(token, info); 40 | if(matches) { 41 | passwordRetryCache.remove(username); 42 | }else { 43 | throw new IncorrectCredentialsException("密码错误,已错误" + retryCount.get() + "次,最多错误" + retryMax + "次"); 44 | } 45 | return true; 46 | } 47 | 48 | public int getRetryMax() { 49 | return retryMax; 50 | } 51 | 52 | public void setRetryMax(int retryMax) { 53 | this.retryMax = retryMax; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/ShiroAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import com.millinch.spring.boot.autoconfigure.shiro.annotation.EnableShiroWebSupport; 4 | import org.apache.shiro.authc.credential.CredentialsMatcher; 5 | import org.apache.shiro.cache.CacheManager; 6 | import org.apache.shiro.cache.ehcache.EhCacheManager; 7 | import org.apache.shiro.crypto.CipherService; 8 | import org.apache.shiro.io.Serializer; 9 | import org.apache.shiro.mgt.RememberMeManager; 10 | import org.apache.shiro.mgt.SecurityManager; 11 | import org.apache.shiro.realm.AuthenticatingRealm; 12 | import org.apache.shiro.realm.Realm; 13 | import org.apache.shiro.realm.jdbc.JdbcRealm; 14 | import org.apache.shiro.session.SessionListener; 15 | import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler; 16 | import org.apache.shiro.session.mgt.SessionValidationScheduler; 17 | import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO; 18 | import org.apache.shiro.session.mgt.eis.SessionDAO; 19 | import org.apache.shiro.session.mgt.eis.SessionIdGenerator; 20 | import org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler; 21 | import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 22 | import org.apache.shiro.subject.PrincipalCollection; 23 | import org.apache.shiro.web.mgt.CookieRememberMeManager; 24 | import org.apache.shiro.web.servlet.Cookie; 25 | import org.apache.shiro.web.servlet.SimpleCookie; 26 | import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; 27 | import org.apache.shiro.web.session.mgt.WebSessionManager; 28 | import org.springframework.beans.BeanUtils; 29 | import org.springframework.beans.factory.annotation.Autowired; 30 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 31 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 32 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 33 | import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 34 | import org.springframework.boot.context.embedded.FilterRegistrationBean; 35 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 36 | import org.springframework.context.annotation.Bean; 37 | import org.springframework.context.annotation.Configuration; 38 | import org.springframework.context.annotation.DependsOn; 39 | import org.springframework.context.annotation.Import; 40 | 41 | import javax.servlet.Filter; 42 | import javax.sql.DataSource; 43 | import java.util.Collection; 44 | import java.util.LinkedHashMap; 45 | import java.util.Map; 46 | 47 | /** 48 | * This guy is lazy, nothing left. 49 | * 50 | * @author John Zhang 51 | */ 52 | @Configuration 53 | @EnableShiroWebSupport 54 | @ConditionalOnWebApplication 55 | @Import(ShiroConfiguration.class) 56 | @EnableConfigurationProperties({ 57 | ShiroProperties.class, ShiroSignInProperties.class, 58 | ShiroCookieProperties.class, ShiroSessionProperties.class, 59 | ShiroJdbcRealmProperties.class 60 | }) 61 | public class ShiroAutoConfiguration { 62 | 63 | @Autowired 64 | private ShiroProperties properties; 65 | 66 | @Autowired 67 | private ShiroSignInProperties signInProperties; 68 | 69 | @Autowired 70 | private ShiroCookieProperties shiroCookieProperties; 71 | 72 | @Autowired 73 | private ShiroSessionProperties shiroSessionProperties; 74 | 75 | @Autowired 76 | private ShiroJdbcRealmProperties shiroJdbcRealmProperties; 77 | 78 | @Autowired(required = false) 79 | private CipherService cipherService; 80 | 81 | @Autowired(required = false) 82 | private Serializer serializer; 83 | 84 | @Autowired(required = false) 85 | private Collection listeners; 86 | 87 | @Autowired(required = false) 88 | private JdbcPermissionDefinitionsLoader jdbcPermissionDefinitionsLoader; 89 | 90 | @Autowired(required = false) 91 | private ShiroFilterCustomizer shiroFilterCustomizer; 92 | 93 | @Bean(name = "mainRealm") 94 | @ConditionalOnMissingBean(name = "mainRealm") 95 | @ConditionalOnProperty(prefix = "shiro.realm.jdbc", name = "enabled", havingValue = "true") 96 | @DependsOn(value = {"dataSource", "lifecycleBeanPostProcessor", "credentialsMatcher"}) 97 | public Realm jdbcRealm(DataSource dataSource, CredentialsMatcher credentialsMatcher) { 98 | JdbcRealm jdbcRealm = new JdbcRealm(); 99 | if (shiroJdbcRealmProperties.getAuthenticationQuery() != null) { 100 | jdbcRealm.setAuthenticationQuery(shiroJdbcRealmProperties.getAuthenticationQuery()); 101 | } 102 | if (shiroJdbcRealmProperties.getUserRolesQuery() != null) { 103 | jdbcRealm.setUserRolesQuery(shiroJdbcRealmProperties.getUserRolesQuery()); 104 | } 105 | if (shiroJdbcRealmProperties.getPermissionsQuery() != null) { 106 | jdbcRealm.setPermissionsQuery(shiroJdbcRealmProperties.getPermissionsQuery()); 107 | } 108 | if (shiroJdbcRealmProperties.getSalt() != null) { 109 | jdbcRealm.setSaltStyle(shiroJdbcRealmProperties.getSalt()); 110 | } 111 | jdbcRealm.setPermissionsLookupEnabled(shiroJdbcRealmProperties.isPermissionsLookupEnabled()); 112 | jdbcRealm.setDataSource(dataSource); 113 | jdbcRealm.setCredentialsMatcher(credentialsMatcher); 114 | return jdbcRealm; 115 | } 116 | 117 | @Bean(name = "mainRealm") 118 | @ConditionalOnMissingBean(name = "mainRealm") 119 | @DependsOn(value = {"lifecycleBeanPostProcessor", "credentialsMatcher"}) 120 | @ConditionalOnProperty(prefix = "shiro.realm.jdbc", name = "enabled", matchIfMissing = true) 121 | public Realm realm(CredentialsMatcher credentialsMatcher) { 122 | Class realmClass = properties.getRealmClass(); 123 | Realm realm = (Realm) BeanUtils.instantiate(realmClass); 124 | if (realm instanceof AuthenticatingRealm) { 125 | ((AuthenticatingRealm) realm).setCredentialsMatcher(credentialsMatcher); 126 | } 127 | return realm; 128 | } 129 | 130 | @Bean(name = "cacheManager") 131 | @ConditionalOnClass(name = {"org.apache.shiro.cache.ehcache.EhCacheManager"}) 132 | @ConditionalOnMissingBean(name = "cacheManager") 133 | public CacheManager ehcacheManager() { 134 | EhCacheManager ehCacheManager = new EhCacheManager(); 135 | ShiroProperties.Ehcache ehcache = properties.getEhcache(); 136 | if (ehcache.getCacheManagerConfigFile() != null) { 137 | ehCacheManager.setCacheManagerConfigFile(ehcache.getCacheManagerConfigFile()); 138 | } 139 | return ehCacheManager; 140 | } 141 | 142 | @Bean 143 | @ConditionalOnMissingBean(Cookie.class) 144 | public Cookie rememberMeCookie() { 145 | SimpleCookie rememberMeCookie = new SimpleCookie(); 146 | rememberMeCookie.setName(signInProperties.getRememberMeParam()); 147 | rememberMeCookie.setMaxAge(shiroCookieProperties.getMaxAge()); 148 | rememberMeCookie.setValue(shiroCookieProperties.getValue()); 149 | rememberMeCookie.setVersion(shiroCookieProperties.getVersion()); 150 | rememberMeCookie.setHttpOnly(shiroCookieProperties.isHttpOnly()); 151 | rememberMeCookie.setSecure(shiroCookieProperties.isSecure()); 152 | return rememberMeCookie; 153 | } 154 | 155 | @Bean 156 | @ConditionalOnMissingBean(RememberMeManager.class) 157 | public RememberMeManager rememberMeManager(Cookie cookie) { 158 | CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); 159 | cookieRememberMeManager.setCookie(cookie); 160 | cookieRememberMeManager.setCipherService(cipherService); 161 | if (shiroCookieProperties.getCipherKey() != null) { 162 | cookieRememberMeManager.setCipherKey(shiroCookieProperties.getCipherKey().getBytes()); 163 | } else { 164 | if (shiroCookieProperties.getEncryptionCipherKey() != null) { 165 | cookieRememberMeManager.setEncryptionCipherKey(shiroCookieProperties.getEncryptionCipherKey().getBytes()); 166 | } 167 | if (shiroCookieProperties.getDecryptionCipherKey() != null) { 168 | cookieRememberMeManager.setDecryptionCipherKey(shiroCookieProperties.getDecryptionCipherKey().getBytes()); 169 | } 170 | } 171 | cookieRememberMeManager.setSerializer(serializer); 172 | return cookieRememberMeManager; 173 | } 174 | 175 | @Bean 176 | @ConditionalOnMissingBean 177 | public SessionDAO sessionDAO(CacheManager cacheManager) { 178 | EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO(); 179 | sessionDAO.setActiveSessionsCacheName(shiroSessionProperties.getActiveSessionsCacheName()); 180 | Class idGenerator = shiroSessionProperties.getIdGenerator(); 181 | if (idGenerator != null) { 182 | SessionIdGenerator sessionIdGenerator = BeanUtils.instantiate(idGenerator); 183 | sessionDAO.setSessionIdGenerator(sessionIdGenerator); 184 | } 185 | sessionDAO.setCacheManager(cacheManager); 186 | return sessionDAO; 187 | } 188 | 189 | @Bean(name = "sessionValidationScheduler") 190 | @DependsOn(value = {"sessionManager"}) 191 | @ConditionalOnClass(name = {"org.quartz.Scheduler"}) 192 | @ConditionalOnMissingBean(SessionValidationScheduler.class) 193 | public SessionValidationScheduler quartzSessionValidationScheduler(DefaultWebSessionManager sessionManager) { 194 | QuartzSessionValidationScheduler quartzSessionValidationScheduler = new QuartzSessionValidationScheduler(sessionManager); 195 | sessionManager.setDeleteInvalidSessions(shiroSessionProperties.isDeleteInvalidSessions()); 196 | sessionManager.setSessionValidationInterval(shiroSessionProperties.getValidationInterval()); 197 | sessionManager.setSessionValidationSchedulerEnabled(shiroSessionProperties.isValidationSchedulerEnabled()); 198 | sessionManager.setSessionValidationScheduler(quartzSessionValidationScheduler); 199 | return quartzSessionValidationScheduler; 200 | } 201 | 202 | @Bean(name = "sessionValidationScheduler") 203 | @DependsOn(value = {"sessionManager"}) 204 | @ConditionalOnMissingBean(SessionValidationScheduler.class) 205 | public SessionValidationScheduler sessionValidationScheduler(DefaultWebSessionManager sessionManager) { 206 | ExecutorServiceSessionValidationScheduler validationScheduler = new ExecutorServiceSessionValidationScheduler(sessionManager); 207 | sessionManager.setDeleteInvalidSessions(shiroSessionProperties.isDeleteInvalidSessions()); 208 | sessionManager.setSessionValidationInterval(shiroSessionProperties.getValidationInterval()); 209 | sessionManager.setSessionValidationSchedulerEnabled(shiroSessionProperties.isValidationSchedulerEnabled()); 210 | sessionManager.setSessionValidationScheduler(validationScheduler); 211 | return validationScheduler; 212 | } 213 | 214 | @Bean 215 | @DependsOn(value = {"cacheManager", "sessionDAO"}) 216 | public WebSessionManager sessionManager(CacheManager cacheManager, SessionDAO sessionDAO) { 217 | DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); 218 | sessionManager.setCacheManager(cacheManager); 219 | sessionManager.setGlobalSessionTimeout(shiroSessionProperties.getGlobalSessionTimeout()); 220 | 221 | sessionManager.setSessionDAO(sessionDAO); 222 | sessionManager.setSessionListeners(listeners); 223 | return sessionManager; 224 | } 225 | 226 | @Bean(name = "credentialsMatcher") 227 | @ConditionalOnMissingBean 228 | @DependsOn("cacheManager") 229 | public CredentialsMatcher credentialsMatcher(CacheManager cacheManager) { 230 | RetryLimitHashedCredentialsMatcher credentialsMatcher = new RetryLimitHashedCredentialsMatcher(cacheManager); 231 | credentialsMatcher.setHashAlgorithmName(properties.getHashAlgorithmName()); 232 | credentialsMatcher.setHashIterations(properties.getHashIterations()); 233 | credentialsMatcher.setStoredCredentialsHexEncoded(properties.isStoredCredentialsHexEncoded()); 234 | credentialsMatcher.setRetryMax(properties.getRetryMax()); 235 | return credentialsMatcher; 236 | } 237 | 238 | public FormSignInFilter formSignInFilter() { 239 | FormSignInFilter filter = new FormSignInFilter(); 240 | filter.setLoginUrl(properties.getLoginUrl()); 241 | filter.setSuccessUrl(properties.getSuccessUrl()); 242 | filter.setUsernameParam(signInProperties.getUserParam()); 243 | filter.setPasswordParam(signInProperties.getPasswordParam()); 244 | filter.setRememberMeParam(signInProperties.getRememberMeParam()); 245 | return filter; 246 | } 247 | 248 | public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager) throws Exception { 249 | ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); 250 | shiroFilter.setSecurityManager(securityManager); 251 | shiroFilter.setLoginUrl(properties.getLoginUrl()); 252 | shiroFilter.setSuccessUrl(properties.getSuccessUrl()); 253 | shiroFilter.setUnauthorizedUrl(properties.getUnauthorizedUrl()); 254 | 255 | Map filterMap = new LinkedHashMap(); 256 | filterMap.put("authc", formSignInFilter()); 257 | 258 | Map filterClasses = instantiateFilterClasses(properties.getFilters()); 259 | if (filterClasses != null) { 260 | filterMap.putAll(filterClasses); 261 | } 262 | 263 | if (shiroFilterCustomizer != null) { 264 | filterMap = shiroFilterCustomizer.customize(filterMap); 265 | } 266 | 267 | shiroFilter.setFilters(filterMap); 268 | 269 | Map filterChains = new LinkedHashMap<>(); 270 | if (jdbcPermissionDefinitionsLoader != null) { 271 | Map permissionUrlMap = jdbcPermissionDefinitionsLoader.getObject(); 272 | filterChains.putAll(permissionUrlMap); 273 | } 274 | if (properties.getFilterChainDefinitions() != null) { 275 | filterChains.putAll(properties.getFilterChainDefinitions()); 276 | } 277 | shiroFilter.setFilterChainDefinitionMap(filterChains); 278 | return shiroFilter; 279 | } 280 | 281 | private Map instantiateFilterClasses(Map> filters) { 282 | Map filterMap = null; 283 | if (filters != null) { 284 | filterMap = new LinkedHashMap(); 285 | for (String name : filters.keySet()) { 286 | Class clazz = filters.get(name); 287 | Filter f = BeanUtils.instantiate(clazz); 288 | filterMap.put(name, f); 289 | } 290 | } 291 | return filterMap; 292 | } 293 | 294 | @Bean(name = "shiroFilter") 295 | @DependsOn("securityManager") 296 | @ConditionalOnMissingBean 297 | public FilterRegistrationBean filterRegistrationBean(SecurityManager securityManager) throws Exception { 298 | FilterRegistrationBean filterRegistration = new FilterRegistrationBean(); 299 | //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 300 | filterRegistration.addInitParameter("targetFilterLifecycle", "true"); 301 | filterRegistration.setFilter((Filter) getShiroFilterFactoryBean(securityManager).getObject()); 302 | filterRegistration.setEnabled(true); 303 | filterRegistration.addUrlPatterns("/*"); 304 | return filterRegistration; 305 | } 306 | 307 | @Bean 308 | @ConditionalOnMissingBean 309 | @ConditionalOnProperty(prefix = "shiro", name = "filterChainSql") 310 | public JdbcPermissionDefinitionsLoader jdbcFilterChainsLoader(DataSource dataSource) { 311 | JdbcPermissionDefinitionsLoader jdbcPermissionDefinitionsLoader = new JdbcPermissionDefinitionsLoader(dataSource); 312 | jdbcPermissionDefinitionsLoader.setSql(properties.getFilterChainSql()); 313 | return jdbcPermissionDefinitionsLoader; 314 | } 315 | 316 | } 317 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/ShiroConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import org.apache.shiro.cache.CacheManager; 4 | import org.apache.shiro.cache.MemoryConstrainedCacheManager; 5 | import org.apache.shiro.cache.ehcache.EhCacheManager; 6 | import org.apache.shiro.mgt.DefaultSecurityManager; 7 | import org.apache.shiro.mgt.RememberMeManager; 8 | import org.apache.shiro.realm.Realm; 9 | import org.apache.shiro.session.mgt.SessionManager; 10 | import org.apache.shiro.spring.LifecycleBeanPostProcessor; 11 | import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; 12 | import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 13 | import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; 14 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 15 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 16 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.DependsOn; 20 | import org.springframework.core.annotation.Order; 21 | 22 | /** 23 | * This guy is lazy, nothing left. 24 | * 25 | * @author John Zhang 26 | */ 27 | public class ShiroConfiguration { 28 | 29 | @Bean(name = "lifecycleBeanPostProcessor") 30 | @ConditionalOnMissingBean 31 | public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { 32 | return new LifecycleBeanPostProcessor(); 33 | } 34 | 35 | @ConditionalOnMissingBean 36 | @Bean(name = "defaultAdvisorAutoProxyCreator") 37 | @DependsOn("lifecycleBeanPostProcessor") 38 | public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { 39 | DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); 40 | defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); 41 | return defaultAdvisorAutoProxyCreator; 42 | } 43 | 44 | /** 45 | * (基于内存的)用户授权信息Cache 46 | */ 47 | @Bean(name = "cacheManager") 48 | @ConditionalOnMissingBean(name = "cacheManager") 49 | @ConditionalOnMissingClass(value = {"org.apache.shiro.cache.ehcache.EhCacheManager"}) 50 | public CacheManager cacheManager() { 51 | return new MemoryConstrainedCacheManager(); 52 | } 53 | 54 | @Bean(name = "securityManager") 55 | @DependsOn(value = {"cacheManager", "rememberMeManager", "mainRealm"}) 56 | public DefaultSecurityManager securityManager(Realm realm, RememberMeManager rememberMeManager, 57 | CacheManager cacheManager, SessionManager sessionManager) { 58 | DefaultSecurityManager sm = new DefaultWebSecurityManager(); 59 | sm.setRealm(realm); 60 | sm.setCacheManager(cacheManager); 61 | sm.setSessionManager(sessionManager); 62 | sm.setRememberMeManager(rememberMeManager); 63 | return sm; 64 | } 65 | 66 | @Bean 67 | @ConditionalOnMissingBean 68 | public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager) { 69 | AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor(); 70 | aasa.setSecurityManager(securityManager); 71 | return aasa; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/ShiroCookieProperties.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * This guy is lazy, nothing left. 7 | * 8 | * @author John Zhang 9 | */ 10 | @ConfigurationProperties(prefix = "shiro.cookie") 11 | public class ShiroCookieProperties { 12 | 13 | private String name = "rememberMe"; 14 | 15 | private String value; 16 | 17 | private int maxAge = 60 * 60 * 24 * 365; // one year 18 | 19 | private int version = -1; 20 | 21 | private boolean secure; 22 | 23 | private boolean httpOnly = true; 24 | 25 | private String cipherKey; 26 | 27 | private String encryptionCipherKey; 28 | 29 | private String decryptionCipherKey; 30 | 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | } 38 | 39 | public String getValue() { 40 | return value; 41 | } 42 | 43 | public void setValue(String value) { 44 | this.value = value; 45 | } 46 | 47 | public int getMaxAge() { 48 | return maxAge; 49 | } 50 | 51 | public void setMaxAge(int maxAge) { 52 | this.maxAge = maxAge; 53 | } 54 | 55 | public int getVersion() { 56 | return version; 57 | } 58 | 59 | public void setVersion(int version) { 60 | this.version = version; 61 | } 62 | 63 | public boolean isSecure() { 64 | return secure; 65 | } 66 | 67 | public void setSecure(boolean secure) { 68 | this.secure = secure; 69 | } 70 | 71 | public boolean isHttpOnly() { 72 | return httpOnly; 73 | } 74 | 75 | public void setHttpOnly(boolean httpOnly) { 76 | this.httpOnly = httpOnly; 77 | } 78 | 79 | public String getCipherKey() { 80 | return cipherKey; 81 | } 82 | 83 | public void setCipherKey(String cipherKey) { 84 | this.cipherKey = cipherKey; 85 | } 86 | 87 | public String getEncryptionCipherKey() { 88 | return encryptionCipherKey; 89 | } 90 | 91 | public void setEncryptionCipherKey(String encryptionCipherKey) { 92 | this.encryptionCipherKey = encryptionCipherKey; 93 | } 94 | 95 | public String getDecryptionCipherKey() { 96 | return decryptionCipherKey; 97 | } 98 | 99 | public void setDecryptionCipherKey(String decryptionCipherKey) { 100 | this.decryptionCipherKey = decryptionCipherKey; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/ShiroFilterCustomizer.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import javax.servlet.Filter; 4 | import java.util.Map; 5 | 6 | /** 7 | * This guy is lazy, nothing left. 8 | * 9 | * @author John Zhang 10 | */ 11 | public interface ShiroFilterCustomizer { 12 | 13 | Map customize(Map filterMap); 14 | } 15 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/ShiroJdbcRealmProperties.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import org.apache.shiro.realm.jdbc.JdbcRealm; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * This guy is lazy, nothing left. 8 | * 9 | * @author John Zhang 10 | */ 11 | @ConfigurationProperties(prefix = "shiro.realm.jdbc") 12 | public class ShiroJdbcRealmProperties { 13 | 14 | private boolean enabled; 15 | 16 | private JdbcRealm.SaltStyle salt; 17 | 18 | /** 19 | * select password from users where username = ? 20 | */ 21 | private String authenticationQuery; 22 | 23 | /** 24 | * select role_name from user_roles where username = ? 25 | */ 26 | private String userRolesQuery; 27 | 28 | /** 29 | * select permission from roles_permissions where role_name = ? 30 | */ 31 | private String permissionsQuery; 32 | 33 | private boolean permissionsLookupEnabled = true; 34 | 35 | public boolean isEnabled() { 36 | return enabled; 37 | } 38 | 39 | public void setEnabled(boolean enabled) { 40 | this.enabled = enabled; 41 | } 42 | 43 | public String getAuthenticationQuery() { 44 | return authenticationQuery; 45 | } 46 | 47 | public void setAuthenticationQuery(String authenticationQuery) { 48 | this.authenticationQuery = authenticationQuery; 49 | } 50 | 51 | public String getUserRolesQuery() { 52 | return userRolesQuery; 53 | } 54 | 55 | public void setUserRolesQuery(String userRolesQuery) { 56 | this.userRolesQuery = userRolesQuery; 57 | } 58 | 59 | public String getPermissionsQuery() { 60 | return permissionsQuery; 61 | } 62 | 63 | public void setPermissionsQuery(String permissionsQuery) { 64 | this.permissionsQuery = permissionsQuery; 65 | } 66 | 67 | public JdbcRealm.SaltStyle getSalt() { 68 | return salt; 69 | } 70 | 71 | public void setSalt(JdbcRealm.SaltStyle salt) { 72 | this.salt = salt; 73 | } 74 | 75 | public boolean isPermissionsLookupEnabled() { 76 | return permissionsLookupEnabled; 77 | } 78 | 79 | public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) { 80 | this.permissionsLookupEnabled = permissionsLookupEnabled; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/ShiroProperties.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import org.apache.shiro.realm.Realm; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | import javax.servlet.Filter; 7 | import java.util.Map; 8 | 9 | /** 10 | * Configuration properties for Shiro. 11 | * 12 | * @author John Zhang 13 | */ 14 | @ConfigurationProperties(prefix = "shiro") 15 | public class ShiroProperties { 16 | /** 17 | * Custom Realm 18 | */ 19 | private Class realmClass = null; 20 | 21 | /** 22 | * URL of login 23 | */ 24 | private String loginUrl = "/login"; 25 | /** 26 | * URL of success 27 | */ 28 | private String successUrl = "/index"; 29 | /** 30 | * URL of unauthorized 31 | */ 32 | private String unauthorizedUrl = "/unauthorized"; 33 | 34 | private String hashAlgorithmName = "MD5"; 35 | 36 | private int hashIterations = 1; 37 | 38 | /** 39 | * 密码重试次数上限 40 | */ 41 | private int retryMax = 5; 42 | 43 | private boolean storedCredentialsHexEncoded = true; 44 | 45 | /** 46 | * Shiro managed filters 47 | */ 48 | private Map> filters; 49 | 50 | /** 51 | * Filter chain 52 | */ 53 | private Map filterChainDefinitions; 54 | 55 | private String filterChainSql; 56 | 57 | private final Ehcache ehcache = new Ehcache(); 58 | 59 | public Class getRealmClass() { 60 | return realmClass; 61 | } 62 | 63 | public void setRealmClass(Class realmClass) { 64 | this.realmClass = realmClass; 65 | } 66 | 67 | public String getLoginUrl() { 68 | return loginUrl; 69 | } 70 | 71 | public void setLoginUrl(String loginUrl) { 72 | this.loginUrl = loginUrl; 73 | } 74 | 75 | public String getSuccessUrl() { 76 | return successUrl; 77 | } 78 | 79 | public void setSuccessUrl(String successUrl) { 80 | this.successUrl = successUrl; 81 | } 82 | 83 | public String getUnauthorizedUrl() { 84 | return unauthorizedUrl; 85 | } 86 | 87 | public void setUnauthorizedUrl(String unauthorizedUrl) { 88 | this.unauthorizedUrl = unauthorizedUrl; 89 | } 90 | 91 | public String getHashAlgorithmName() { 92 | return hashAlgorithmName; 93 | } 94 | 95 | public void setHashAlgorithmName(String hashAlgorithmName) { 96 | this.hashAlgorithmName = hashAlgorithmName; 97 | } 98 | 99 | public int getHashIterations() { 100 | return hashIterations; 101 | } 102 | 103 | public void setHashIterations(int hashIterations) { 104 | this.hashIterations = hashIterations; 105 | } 106 | 107 | public int getRetryMax() { 108 | return retryMax; 109 | } 110 | 111 | public void setRetryMax(int retryMax) { 112 | this.retryMax = retryMax; 113 | } 114 | 115 | public boolean isStoredCredentialsHexEncoded() { 116 | return storedCredentialsHexEncoded; 117 | } 118 | 119 | public void setStoredCredentialsHexEncoded(boolean storedCredentialsHexEncoded) { 120 | this.storedCredentialsHexEncoded = storedCredentialsHexEncoded; 121 | } 122 | 123 | public Map> getFilters() { 124 | return filters; 125 | } 126 | 127 | public void setFilters(Map> filters) { 128 | this.filters = filters; 129 | } 130 | 131 | public Map getFilterChainDefinitions() { 132 | return filterChainDefinitions; 133 | } 134 | 135 | public void setFilterChainDefinitions(Map filterChainDefinitions) { 136 | this.filterChainDefinitions = filterChainDefinitions; 137 | } 138 | 139 | public String getFilterChainSql() { 140 | return filterChainSql; 141 | } 142 | 143 | public void setFilterChainSql(String filterChainSql) { 144 | this.filterChainSql = filterChainSql; 145 | } 146 | 147 | public Ehcache getEhcache() { 148 | return ehcache; 149 | } 150 | 151 | public static class Ehcache { 152 | 153 | private String cacheManagerConfigFile = "classpath:org/apache/shiro/cache/ehcache/ehcache.xml"; 154 | 155 | public String getCacheManagerConfigFile() { 156 | return cacheManagerConfigFile; 157 | } 158 | 159 | public void setCacheManagerConfigFile(String cacheManagerConfigFile) { 160 | this.cacheManagerConfigFile = cacheManagerConfigFile; 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/ShiroSessionProperties.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator; 4 | import org.apache.shiro.session.mgt.eis.SessionIdGenerator; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | 7 | /** 8 | * This guy is lazy, nothing left. 9 | * 10 | * @author John Zhang 11 | */ 12 | @ConfigurationProperties(prefix = "shiro.session") 13 | public class ShiroSessionProperties { 14 | 15 | private long globalSessionTimeout = 30 * 60 * 1000L; 16 | 17 | private boolean deleteInvalidSessions = true; 18 | 19 | private long validationInterval = 60 * 60 * 1000L; 20 | 21 | private boolean validationSchedulerEnabled = true; 22 | 23 | private String activeSessionsCacheName = "shiro-activeSessionCache"; 24 | 25 | private Class idGenerator = JavaUuidSessionIdGenerator.class; 26 | 27 | public long getGlobalSessionTimeout() { 28 | return globalSessionTimeout; 29 | } 30 | 31 | public void setGlobalSessionTimeout(long globalSessionTimeout) { 32 | this.globalSessionTimeout = globalSessionTimeout; 33 | } 34 | 35 | public boolean isDeleteInvalidSessions() { 36 | return deleteInvalidSessions; 37 | } 38 | 39 | public void setDeleteInvalidSessions(boolean deleteInvalidSessions) { 40 | this.deleteInvalidSessions = deleteInvalidSessions; 41 | } 42 | 43 | public long getValidationInterval() { 44 | return validationInterval; 45 | } 46 | 47 | public void setValidationInterval(long validationInterval) { 48 | this.validationInterval = validationInterval; 49 | } 50 | 51 | public boolean isValidationSchedulerEnabled() { 52 | return validationSchedulerEnabled; 53 | } 54 | 55 | public void setValidationSchedulerEnabled(boolean validationSchedulerEnabled) { 56 | this.validationSchedulerEnabled = validationSchedulerEnabled; 57 | } 58 | 59 | public String getActiveSessionsCacheName() { 60 | return activeSessionsCacheName; 61 | } 62 | 63 | public void setActiveSessionsCacheName(String activeSessionsCacheName) { 64 | this.activeSessionsCacheName = activeSessionsCacheName; 65 | } 66 | 67 | public Class getIdGenerator() { 68 | return idGenerator; 69 | } 70 | 71 | public void setIdGenerator(Class idGenerator) { 72 | this.idGenerator = idGenerator; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/ShiroSignInProperties.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * Configuration properties for Shiro FormAuthenticationFilter 7 | * 8 | * @author John Zhang 9 | */ 10 | @ConfigurationProperties(prefix = "shiro.sign-in") 11 | public class ShiroSignInProperties { 12 | /** 13 | * Request parameter's name of user 14 | */ 15 | private String userParam = "username"; 16 | 17 | /** 18 | * Request parameter's name of password 19 | */ 20 | private String passwordParam = "password"; 21 | 22 | private String rememberMeParam = "rememberMe"; 23 | 24 | public String getUserParam() { 25 | return userParam; 26 | } 27 | 28 | public void setUserParam(String userParam) { 29 | this.userParam = userParam; 30 | } 31 | 32 | public String getPasswordParam() { 33 | return passwordParam; 34 | } 35 | 36 | public void setPasswordParam(String passwordParam) { 37 | this.passwordParam = passwordParam; 38 | } 39 | 40 | public String getRememberMeParam() { 41 | return rememberMeParam; 42 | } 43 | 44 | public void setRememberMeParam(String rememberMeParam) { 45 | this.rememberMeParam = rememberMeParam; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/ShiroWebMvcConfigurerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro; 2 | 3 | import com.millinch.spring.boot.autoconfigure.shiro.annotation.SessionUserArgumentResolver; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * This guy is lazy, nothing left. 12 | * 13 | * @author John Zhang 14 | */ 15 | @Configuration 16 | public class ShiroWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter { 17 | 18 | @Override 19 | public void addArgumentResolvers(List argumentResolvers) { 20 | argumentResolvers.add(new SessionUserArgumentResolver()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/annotation/EnableShiroWebSupport.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro.annotation; 2 | 3 | import com.millinch.spring.boot.autoconfigure.shiro.ShiroWebMvcConfigurerAdapter; 4 | import org.springframework.context.annotation.Import; 5 | import org.springframework.context.annotation.ImportSelector; 6 | import org.springframework.core.type.AnnotationMetadata; 7 | 8 | import java.lang.annotation.*; 9 | 10 | /** 11 | * Annotation to automatically register the following beans for usage with Spring MVC. 12 | *
    13 | *
  • 14 | * {@link com.millinch.spring.boot.autoconfigure.shiro.annotation.SessionUser}. 15 | *
  • 16 | *
17 | * @author John Zhang 18 | */ 19 | @Retention(RetentionPolicy.RUNTIME) 20 | @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) 21 | @Inherited 22 | @Import({ EnableShiroWebSupport.ShiroWebMvcConfigurerAdapterImportSelector.class }) 23 | public @interface EnableShiroWebSupport { 24 | 25 | static class ShiroWebMvcConfigurerAdapterImportSelector implements ImportSelector { 26 | 27 | @Override 28 | public String[] selectImports(AnnotationMetadata importingClassMetadata) { 29 | return new String[] { ShiroWebMvcConfigurerAdapter.class.getName() }; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/annotation/SessionUser.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 获取Shiro当前用户 7 | * @author 张劲航 8 | * @see SessionUserArgumentResolver 9 | */ 10 | @Target({ElementType.PARAMETER}) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Documented 13 | public @interface SessionUser { 14 | } 15 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/java/com/millinch/spring/boot/autoconfigure/shiro/annotation/SessionUserArgumentResolver.java: -------------------------------------------------------------------------------- 1 | package com.millinch.spring.boot.autoconfigure.shiro.annotation; 2 | 3 | import org.apache.shiro.SecurityUtils; 4 | import org.apache.shiro.subject.Subject; 5 | import org.springframework.core.MethodParameter; 6 | import org.springframework.web.bind.support.WebDataBinderFactory; 7 | import org.springframework.web.context.request.NativeWebRequest; 8 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 9 | import org.springframework.web.method.support.ModelAndViewContainer; 10 | 11 | /** 12 | * {@link SessionUser}注解的解析 13 | * @author 张劲航 14 | */ 15 | public class SessionUserArgumentResolver implements HandlerMethodArgumentResolver { 16 | 17 | @Override 18 | public boolean supportsParameter(MethodParameter parameter) { 19 | return parameter.hasParameterAnnotation(SessionUser.class); 20 | } 21 | 22 | @Override 23 | public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 24 | Subject subject = SecurityUtils.getSubject(); 25 | if(supportsParameter(parameter) && subject.isAuthenticated()){ 26 | return subject.getPrincipal(); 27 | } 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spring-boot-shiro-autoconfigure/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | # Auto Configure 2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 3 | com.millinch.spring.boot.autoconfigure.shiro.ShiroAutoConfiguration -------------------------------------------------------------------------------- /spring-boot-shiro-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 1.3.5.RELEASE 10 | 11 | com.millinch 12 | spring-boot-shiro-sample 13 | 1.0-SNAPSHOT 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-web 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | com.millinch 27 | spring-boot-shiro-starter 28 | 1.0.0 29 | 30 | 31 | mysql 32 | mysql-connector-java 33 | 5.1.38 34 | 35 | 36 | joda-time 37 | joda-time 38 | 2.9.4 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-maven-plugin 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/java/com/millinch/springboot/shiro/sample/SampleApplication.java: -------------------------------------------------------------------------------- 1 | package com.millinch.springboot.shiro.sample; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * This guy is lazy, nothing left. 8 | * 9 | * @author John Zhang 10 | */ 11 | @SpringBootApplication 12 | public class SampleApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(SampleApplication.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/java/com/millinch/springboot/shiro/sample/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.millinch.springboot.shiro.sample.domain; 2 | 3 | /** 4 | * This guy is lazy, nothing left. 5 | * @author John Zhang 6 | */ 7 | public class User { 8 | 9 | private String username; 10 | private String password; 11 | 12 | public String getUsername() { 13 | return username; 14 | } 15 | 16 | public void setUsername(String username) { 17 | this.username = username; 18 | } 19 | 20 | public String getPassword() { 21 | return password; 22 | } 23 | 24 | public void setPassword(String password) { 25 | this.password = password; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/java/com/millinch/springboot/shiro/sample/realm/FirstRealm.java: -------------------------------------------------------------------------------- 1 | package com.millinch.springboot.shiro.sample.realm; 2 | 3 | import org.apache.shiro.realm.jdbc.JdbcRealm; 4 | 5 | /** 6 | * This guy is lazy, nothing left. 7 | * 8 | * @author John Zhang 9 | */ 10 | public class FirstRealm extends JdbcRealm { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/java/com/millinch/springboot/shiro/sample/web/MyFilter.java: -------------------------------------------------------------------------------- 1 | package com.millinch.springboot.shiro.sample.web; 2 | 3 | import javax.servlet.*; 4 | import java.io.IOException; 5 | 6 | /** 7 | * This guy is lazy, nothing left. 8 | * 9 | * @author John Zhang 10 | */ 11 | public class MyFilter implements Filter { 12 | @Override 13 | public void init(FilterConfig filterConfig) throws ServletException { 14 | 15 | } 16 | 17 | @Override 18 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 19 | System.out.println("My Filter executed!!"); 20 | filterChain.doFilter(servletRequest, servletResponse); 21 | } 22 | 23 | @Override 24 | public void destroy() { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/java/com/millinch/springboot/shiro/sample/web/SampleApi.java: -------------------------------------------------------------------------------- 1 | package com.millinch.springboot.shiro.sample.web; 2 | 3 | import org.apache.shiro.SecurityUtils; 4 | import org.apache.shiro.subject.Subject; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | 9 | /** 10 | * This guy is lazy, nothing left. 11 | * 12 | * @author John Zhang 13 | */ 14 | @Controller 15 | public class SampleApi { 16 | 17 | @RequestMapping("/role/config") 18 | public ResponseEntity roleConfig() { 19 | return ResponseEntity.ok("Here you are"); 20 | } 21 | 22 | @RequestMapping("/user/addition") 23 | public ResponseEntity userAddition() { 24 | Subject subject = SecurityUtils.getSubject(); 25 | boolean permitted = subject.isPermitted("system:admin:create"); 26 | System.out.println("permitted = " + permitted); 27 | return ResponseEntity.ok("Here you are"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 7788 3 | spring: 4 | datasource: 5 | url: jdbc:mysql://localhost:3306/test 6 | username: root 7 | password: root 8 | driver-class-name: com.mysql.jdbc.Driver 9 | platform: mysql 10 | 11 | shiro: 12 | realm: 13 | jdbc: 14 | enabled: true 15 | authentication-query: SELECT password FROM sys_user where user_name = ? #根据用户名获取密码 16 | salt: no_salt #可选值:no_salt, crypt(源码中未实现), column(上面这个SQL中第二列中获取salt), external(需继承JdbcRealm重写getSaltForUser()方法) 17 | user-roles-query: SELECT r.code 18 | FROM sys_user_role ur 19 | LEFT JOIN sys_role r ON r.id = ur.role_id 20 | LEFT JOIN sys_user u ON ur.user_id = u.id 21 | WHERE u.user_name = ? #根据用户名获取角色 22 | permissions-query: SELECT re.permission 23 | FROM sys_role_resource rr 24 | LEFT JOIn sys_resource re ON rr.resource_id = re.id 25 | LEFT JOIN sys_role r ON rr.role_id = r.id 26 | WHERE r.code = ? #根据角色获取权限 27 | permissions-lookup-enabled: true 28 | login-url: /login.html #登录入口URL 29 | success-url: /index.html #登录成功跳转URL 30 | unauthorized-url: /unauthorized.html #当访问未授权页面时跳转至该URL,将为filter chain中的每个AuthorizationFilter设置跳转URL(如果目标没有指定) 31 | sign-in: 32 | user-param: username #用户名参数名称 33 | password-param: password #密码参数名称 34 | remember-me-param: rememberMe #记住我参数名称 35 | hash-iterations: 1 #加密迭代次数,强制设为至少1次(即使设置0或负数) 36 | hash-algorithm-name: MD5 #加密算法名称,如:MD2/SHA-1/SHA-256/SHA-384/SHA-512 37 | filters: 38 | my-filter: com.millinch.springboot.shiro.sample.web.MyFilter 39 | filter-chain-definitions: #默认为空,一般如下配置 40 | /test-my-filter: my-filter 41 | /logout: logout 42 | /favicon.ico: anon 43 | /assets/**: anon 44 | /**: authc 45 | filter-chain-sql: SELECT r.url, r.permission 46 | FROM sys_resource r 47 | WHERE r.permission != '' AND r.url != '' 48 | logging: 49 | level: debug -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/resources/data-mysql.sql: -------------------------------------------------------------------------------- 1 | -- ---------------------------- 2 | -- Records of sys_user 3 | -- ---------------------------- 4 | INSERT INTO `sys_user` VALUES ('1', '18966668888', 'super', '超级管理员', 'e10adc3949ba59abbe56e057f20f883e', null, null, null, null, '', null, null, null, null, null, '1', null, '2015-09-28 17:47:18', null, '2015-09-30 17:36:16'); 5 | INSERT INTO `sys_user` VALUES ('2', '13988886666', 'admin', '系统管理员A', 'e10adc3949ba59abbe56e057f20f883e', null, null, null, '1234567', 'super@millinch.com', null, null, null, null, null, '1', null, '2015-09-29 17:47:22', null, '2015-09-30 17:32:07'); 6 | 7 | -- ---------------------------- 8 | -- Records of sys_role 9 | -- ---------------------------- 10 | INSERT INTO `sys_role` VALUES ('1', '超级管理员', 'superManager', '拥有所有权限', '1', null, '0', '2015-09-01 14:36:16', null, '2016-01-03 22:29:58'); 11 | INSERT INTO `sys_role` VALUES ('2', '系统管理员', 'systemManager', '拥有部分权限', '1', null, '0', '2015-08-30 18:03:47', null, '2015-08-30 18:03:47'); 12 | INSERT INTO `sys_role` VALUES ('3', '角色1', 'role1', 'nothing 34', '1', null, null, '2015-10-05 18:20:35', null, '2015-10-05 18:35:57'); 13 | 14 | -- ---------------------------- 15 | -- Records of sys_resource 16 | -- ---------------------------- 17 | INSERT INTO `sys_resource` VALUES ('1', '菜单', '系统管理', 'system:*', 'fa fa-dashboard', '1', '', '', '1', '0', '0', '2015-07-01 19:33:21', null, '2015-10-09 10:34:05'); 18 | INSERT INTO `sys_resource` VALUES ('2', '菜单', '角色管理', 'system:role:*', 'fa fa-male', '12', '/role/config', '', '1', '1', '0', '2015-07-01 19:38:38', null, '2015-07-01 19:38:38'); 19 | INSERT INTO `sys_resource` VALUES ('3', '菜单', '密码修改', 'system:password', null, '14', '/user/password/edition', '', '1', '1', '0', '2015-07-01 19:38:51', null, '2015-07-01 19:39:51'); 20 | INSERT INTO `sys_resource` VALUES ('4', '菜单', '操作日志', 'system:log:*', null, '15', '/handle/operation/log', '', '1', '1', '0', '2015-07-01 19:40:37', null, '2015-07-01 19:40:37'); 21 | INSERT INTO `sys_resource` VALUES ('5', 'URL', '新增角色', 'system:role:create', null, '13', '/role/addition', '', '1', '1', '0', '2015-07-01 19:41:21', null, '2015-10-08 16:45:43'); 22 | INSERT INTO `sys_resource` VALUES ('6', '菜单', '用户管理', 'system:admin:*', 'fa fa-users', '11', '/user/config', '', '1', '1', '0', '2015-07-01 19:34:38', null, '2015-07-01 19:34:38'); 23 | INSERT INTO `sys_resource` VALUES ('7', 'URL', '新增用户', 'system:admin:create', '', null, '/user/addition', 'bbb', '1', '0', '0', '2015-08-30 18:29:56', null, '2015-10-09 11:33:03'); 24 | 25 | -- ---------------------------- 26 | -- Records of sys_role_resource 27 | -- ---------------------------- 28 | INSERT INTO `sys_role_resource` VALUES ('1', '1', '1'); 29 | INSERT INTO `sys_role_resource` VALUES ('2', '1', '2'); 30 | INSERT INTO `sys_role_resource` VALUES ('3', '1', '3'); 31 | INSERT INTO `sys_role_resource` VALUES ('4', '1', '4'); 32 | INSERT INTO `sys_role_resource` VALUES ('5', '1', '5'); 33 | INSERT INTO `sys_role_resource` VALUES ('6', '1', '6'); 34 | INSERT INTO `sys_role_resource` VALUES ('7', '1', '7'); 35 | INSERT INTO `sys_role_resource` VALUES ('8', '2', '2'); 36 | 37 | -- ---------------------------- 38 | -- Records of sys_user_role 39 | -- ---------------------------- 40 | INSERT INTO `sys_user_role` VALUES ('1', '1', '1'); 41 | INSERT INTO `sys_user_role` VALUES ('2', '2', '2'); -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/resources/schema-mysql.sql: -------------------------------------------------------------------------------- 1 | -- ---------------------------- 2 | -- Table structure for sys_resource 3 | -- ---------------------------- 4 | DROP TABLE IF EXISTS `sys_resource`; 5 | CREATE TABLE `sys_resource` ( 6 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 7 | `type` varchar(255) DEFAULT '' COMMENT '权限类型:menu、button、url', 8 | `name` varchar(255) NOT NULL COMMENT '权限名称', 9 | `permission` varchar(255) NOT NULL COMMENT '权限字符串', 10 | `icon` varchar(255) DEFAULT NULL, 11 | `sort` int(11) DEFAULT '0', 12 | `url` varchar(255) DEFAULT '', 13 | `description` varchar(255) DEFAULT '' COMMENT '资源描述', 14 | `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态值', 15 | `parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '父ID', 16 | `create_by` bigint(20) DEFAULT NULL, 17 | `create_at` datetime NOT NULL, 18 | `update_by` bigint(20) DEFAULT NULL, 19 | `update_at` datetime DEFAULT NULL, 20 | PRIMARY KEY (`id`), 21 | UNIQUE KEY `uq_resource_type` (`type`,`permission`) USING BTREE, 22 | KEY `create_by` (`create_by`), 23 | KEY `update_by` (`update_by`) 24 | ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='资源(权限)表'; 25 | 26 | 27 | -- ---------------------------- 28 | -- Table structure for sys_role 29 | -- ---------------------------- 30 | DROP TABLE IF EXISTS `sys_role`; 31 | CREATE TABLE `sys_role` ( 32 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 33 | `name` varchar(255) NOT NULL, 34 | `code` varchar(255) NOT NULL, 35 | `remark` varchar(255) DEFAULT '', 36 | `status` tinyint(1) NOT NULL DEFAULT '1', 37 | `parent_id` bigint(20) DEFAULT NULL, 38 | `create_by` bigint(20) DEFAULT NULL, 39 | `create_at` datetime NOT NULL, 40 | `update_by` bigint(20) DEFAULT NULL, 41 | `update_at` datetime DEFAULT NULL, 42 | PRIMARY KEY (`id`), 43 | KEY `create_by` (`create_by`), 44 | KEY `update_by` (`update_by`) 45 | ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 46 | 47 | 48 | -- ---------------------------- 49 | -- Table structure for sys_role_resource 50 | -- ---------------------------- 51 | DROP TABLE IF EXISTS `sys_role_resource`; 52 | CREATE TABLE `sys_role_resource` ( 53 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 54 | `role_id` bigint(20) DEFAULT NULL, 55 | `resource_id` bigint(20) DEFAULT NULL, 56 | PRIMARY KEY (`id`), 57 | KEY `role_id` (`role_id`), 58 | KEY `resource_id` (`resource_id`) 59 | ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; 60 | 61 | -- ---------------------------- 62 | -- Table structure for sys_user 63 | -- ---------------------------- 64 | DROP TABLE IF EXISTS `sys_user`; 65 | CREATE TABLE `sys_user` ( 66 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 67 | `mobile_phone` varchar(255) NOT NULL DEFAULT '' COMMENT '手机号码', 68 | `user_name` varchar(50) DEFAULT NULL COMMENT '用户名', 69 | `nickname` varchar(255) DEFAULT NULL COMMENT '昵称', 70 | `password` varchar(255) NOT NULL COMMENT '密码', 71 | `salt` varchar(255) DEFAULT '' COMMENT '加密混淆字符', 72 | `signature` varchar(255) DEFAULT NULL COMMENT '个性签名', 73 | `gender` tinyint(1) DEFAULT '0' COMMENT '性别', 74 | `qq` bigint(20) DEFAULT NULL COMMENT 'QQ号码', 75 | `email` varchar(255) DEFAULT NULL COMMENT '邮箱地址', 76 | `avatar` varchar(500) DEFAULT '' COMMENT '头像图片路径', 77 | `province` varchar(50) DEFAULT '' COMMENT '省', 78 | `city` varchar(50) DEFAULT '' COMMENT '市', 79 | `reg_ip` varchar(50) DEFAULT NULL COMMENT '注册时IP地址', 80 | `score` int(10) DEFAULT '0' COMMENT '积分值', 81 | `status` int(10) DEFAULT '1' COMMENT '状态:0禁用 1正常', 82 | `create_by` bigint(20) DEFAULT NULL, 83 | `create_at` datetime DEFAULT NULL, 84 | `update_by` bigint(20) DEFAULT NULL, 85 | `update_at` datetime DEFAULT NULL, 86 | PRIMARY KEY (`id`), 87 | UNIQUE KEY `uq_user_name` (`user_name`) 88 | ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='用户表'; 89 | 90 | -- ---------------------------- 91 | -- Table structure for sys_user_role 92 | -- ---------------------------- 93 | DROP TABLE IF EXISTS `sys_user_role`; 94 | CREATE TABLE `sys_user_role` ( 95 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 96 | `user_id` bigint(20) NOT NULL, 97 | `role_id` bigint(20) NOT NULL, 98 | PRIMARY KEY (`id`), 99 | KEY `fk_user_roles` (`user_id`), 100 | KEY `fk_role_users` (`role_id`) 101 | ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; 102 | 103 | -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo - Apache Shiro integration with Spring Boot 6 | 7 | 8 |
9 |
10 |
11 |

Welcome!

12 | 退出 13 |
14 |
15 |
16 | 17 | -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/resources/static/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 |
9 |
10 | 用户名: 11 | 密 码: 12 | 13 |
14 | 15 |
16 | 17 | -------------------------------------------------------------------------------- /spring-boot-shiro-sample/src/main/resources/static/unauthorized.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Unauthorized 6 | 7 | 8 |

没有权限

9 | 10 | -------------------------------------------------------------------------------- /spring-boot-shiro-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.millinch 8 | spring-boot-shiro-starter 9 | 1.0.0 10 | 11 | 12 | 1.3.3.RELEASE 13 | 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-dependencies 20 | ${spring-boot.version} 21 | pom 22 | import 23 | 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter 30 | 31 | 32 | com.millinch 33 | spring-boot-shiro-autoconfigure 34 | 1.0.0 35 | 36 | 37 | -------------------------------------------------------------------------------- /spring-boot-shiro-starter/src/main/resources/META-INF/spring.provides: -------------------------------------------------------------------------------- 1 | provides: spring-boot-shiro-autoconfigure --------------------------------------------------------------------------------