Commit de12fbc897c5f6d55277f450869ff1b4bcc90280

Authored by huhai
2 parents e4f42286 cc677126

Merge branch 'develop' into WMSV2-HH

.gitignore
@@ -10,8 +10,7 @@ target/ @@ -10,8 +10,7 @@ target/
10 *.iws 10 *.iws
11 *.iml 11 *.iml
12 *.ipr 12 *.ipr
13 -#.idea/workspace.xml  
14 -.idea/compiler.xml 13 +
15 14
16 15
17 ### NetBeans ### 16 ### NetBeans ###
huaheng.iml
@@ -195,9 +195,27 @@ @@ -195,9 +195,27 @@
195 <orderEntry type="library" name="Maven: org.unbescape:unbescape:1.1.6.RELEASE" level="project" /> 195 <orderEntry type="library" name="Maven: org.unbescape:unbescape:1.1.6.RELEASE" level="project" />
196 <orderEntry type="library" name="Maven: org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.3.RELEASE" level="project" /> 196 <orderEntry type="library" name="Maven: org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.3.RELEASE" level="project" />
197 <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-devtools:2.1.3.RELEASE" level="project" /> 197 <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-devtools:2.1.3.RELEASE" level="project" />
  198 + <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.1.3.RELEASE" level="project" />
  199 + <orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.1.5.RELEASE" level="project" />
  200 + <orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.1.5.RELEASE" level="project" />
  201 + <orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.1.5.RELEASE" level="project" />
  202 + <orderEntry type="library" name="Maven: org.springframework:spring-tx:5.1.5.RELEASE" level="project" />
  203 + <orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.1.5.RELEASE" level="project" />
  204 + <orderEntry type="library" name="Maven: io.lettuce:lettuce-core:5.1.4.RELEASE" level="project" />
  205 + <orderEntry type="library" name="Maven: io.netty:netty-common:4.1.33.Final" level="project" />
  206 + <orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.33.Final" level="project" />
  207 + <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.33.Final" level="project" />
  208 + <orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.33.Final" level="project" />
  209 + <orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.33.Final" level="project" />
  210 + <orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.33.Final" level="project" />
  211 + <orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.2.6.RELEASE" level="project" />
  212 + <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.2" level="project" />
  213 + <orderEntry type="library" name="Maven: org.apache.commons:commons-pool2:2.6.1" level="project" />
198 <orderEntry type="library" name="Maven: net.sourceforge.nekohtml:nekohtml:1.9.22" level="project" /> 214 <orderEntry type="library" name="Maven: net.sourceforge.nekohtml:nekohtml:1.9.22" level="project" />
199 <orderEntry type="library" name="Maven: xerces:xercesImpl:2.11.0" level="project" /> 215 <orderEntry type="library" name="Maven: xerces:xercesImpl:2.11.0" level="project" />
200 <orderEntry type="library" name="Maven: xml-apis:xml-apis:1.4.01" level="project" /> 216 <orderEntry type="library" name="Maven: xml-apis:xml-apis:1.4.01" level="project" />
  217 + <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" />
  218 + <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.9.8" level="project" />
201 <orderEntry type="library" name="Maven: mysql:mysql-connector-java:8.0.16" level="project" /> 219 <orderEntry type="library" name="Maven: mysql:mysql-connector-java:8.0.16" level="project" />
202 <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.2.12" level="project" /> 220 <orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.2.12" level="project" />
203 <orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.0.1" level="project" /> 221 <orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.0.1" level="project" />
@@ -272,7 +290,6 @@ @@ -272,7 +290,6 @@
272 <orderEntry type="library" name="Maven: org.codehaus.castor:castor-core:1.4.1" level="project" /> 290 <orderEntry type="library" name="Maven: org.codehaus.castor:castor-core:1.4.1" level="project" />
273 <orderEntry type="library" name="Maven: javax.inject:javax.inject:1" level="project" /> 291 <orderEntry type="library" name="Maven: javax.inject:javax.inject:1" level="project" />
274 <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.9.8" level="project" /> 292 <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.9.8" level="project" />
275 - <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.9.8" level="project" />  
276 <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.9.0" level="project" /> 293 <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.9.0" level="project" />
277 <orderEntry type="library" name="Maven: net.sf.jasperreports:jasperreports-fonts:6.10.0" level="project" /> 294 <orderEntry type="library" name="Maven: net.sf.jasperreports:jasperreports-fonts:6.10.0" level="project" />
278 <orderEntry type="library" name="Maven: com.itextpdf:itextpdf:5.5.7" level="project" /> 295 <orderEntry type="library" name="Maven: com.itextpdf:itextpdf:5.5.7" level="project" />
@@ -295,7 +312,6 @@ @@ -295,7 +312,6 @@
295 <orderEntry type="library" name="Maven: org.springframework.amqp:spring-amqp:2.1.4.RELEASE" level="project" /> 312 <orderEntry type="library" name="Maven: org.springframework.amqp:spring-amqp:2.1.4.RELEASE" level="project" />
296 <orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.2.4.RELEASE" level="project" /> 313 <orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.2.4.RELEASE" level="project" />
297 <orderEntry type="library" name="Maven: com.rabbitmq:amqp-client:5.4.3" level="project" /> 314 <orderEntry type="library" name="Maven: com.rabbitmq:amqp-client:5.4.3" level="project" />
298 - <orderEntry type="library" name="Maven: org.springframework:spring-tx:5.1.5.RELEASE" level="project" />  
299 <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-boot-starter:3.1.2" level="project" /> 315 <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-boot-starter:3.1.2" level="project" />
300 <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus:3.1.2" level="project" /> 316 <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus:3.1.2" level="project" />
301 <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-extension:3.1.2" level="project" /> 317 <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-extension:3.1.2" level="project" />
@@ -91,12 +91,30 @@ @@ -91,12 +91,30 @@
91 <optional>true</optional> <!-- 表示依赖不会传递 --> 91 <optional>true</optional> <!-- 表示依赖不会传递 -->
92 </dependency> 92 </dependency>
93 93
  94 + <!-- redis 缓存操作 -->
  95 + <dependency>
  96 + <groupId>org.springframework.boot</groupId>
  97 + <artifactId>spring-boot-starter-data-redis</artifactId>
  98 + </dependency>
  99 +
  100 + <!-- pool 对象池 -->
  101 + <dependency>
  102 + <groupId>org.apache.commons</groupId>
  103 + <artifactId>commons-pool2</artifactId>
  104 + </dependency>
  105 +
94 <!-- thymeleaf网页解析 --> 106 <!-- thymeleaf网页解析 -->
95 <dependency> 107 <dependency>
96 <groupId>net.sourceforge.nekohtml</groupId> 108 <groupId>net.sourceforge.nekohtml</groupId>
97 <artifactId>nekohtml</artifactId> 109 <artifactId>nekohtml</artifactId>
98 </dependency> 110 </dependency>
99 111
  112 + <dependency>
  113 + <groupId>io.jsonwebtoken</groupId>
  114 + <artifactId>jjwt</artifactId>
  115 + <version>0.9.1</version>
  116 + </dependency>
  117 +
100 <!-- Mysql8驱动包 --> 118 <!-- Mysql8驱动包 -->
101 <dependency> 119 <dependency>
102 <groupId>mysql</groupId> 120 <groupId>mysql</groupId>
src/main/java/com/huaheng/framework/config/ShiroConfig.java
@@ -3,6 +3,7 @@ package com.huaheng.framework.config; @@ -3,6 +3,7 @@ package com.huaheng.framework.config;
3 import java.util.LinkedHashMap; 3 import java.util.LinkedHashMap;
4 import java.util.Map; 4 import java.util.Map;
5 import javax.servlet.Filter; 5 import javax.servlet.Filter;
  6 +
6 import org.apache.shiro.cache.ehcache.EhCacheManager; 7 import org.apache.shiro.cache.ehcache.EhCacheManager;
7 import org.apache.shiro.codec.Base64; 8 import org.apache.shiro.codec.Base64;
8 import org.apache.shiro.mgt.SecurityManager; 9 import org.apache.shiro.mgt.SecurityManager;
@@ -28,12 +29,11 @@ import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; @@ -28,12 +29,11 @@ import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
28 29
29 /** 30 /**
30 * 权限配置加载 31 * 权限配置加载
31 - * 32 + *
32 * @author huaheng 33 * @author huaheng
33 */ 34 */
34 @Configuration 35 @Configuration
35 -public class ShiroConfig  
36 -{ 36 +public class ShiroConfig {
37 public static final String PREMISSION_STRING = "perms[\"{0}\"]"; 37 public static final String PREMISSION_STRING = "perms[\"{0}\"]";
38 38
39 // Session超时时间,单位为毫秒(默认30分钟) 39 // Session超时时间,单位为毫秒(默认30分钟)
@@ -84,17 +84,13 @@ public class ShiroConfig @@ -84,17 +84,13 @@ public class ShiroConfig
84 * 缓存管理器 使用Ehcache实现 84 * 缓存管理器 使用Ehcache实现
85 */ 85 */
86 @Bean 86 @Bean
87 - public EhCacheManager getEhCacheManager()  
88 - { 87 + public EhCacheManager getEhCacheManager() {
89 net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("huaheng"); 88 net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("huaheng");
90 EhCacheManager em = new EhCacheManager(); 89 EhCacheManager em = new EhCacheManager();
91 - if (StringUtils.isNull(cacheManager))  
92 - { 90 + if (StringUtils.isNull(cacheManager)) {
93 em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml"); 91 em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml");
94 return em; 92 return em;
95 - }  
96 - else  
97 - { 93 + } else {
98 em.setCacheManager(cacheManager); 94 em.setCacheManager(cacheManager);
99 return em; 95 return em;
100 } 96 }
@@ -104,8 +100,7 @@ public class ShiroConfig @@ -104,8 +100,7 @@ public class ShiroConfig
104 * 自定义Realm 100 * 自定义Realm
105 */ 101 */
106 @Bean 102 @Bean
107 - public UserRealm userRealm(EhCacheManager cacheManager)  
108 - { 103 + public UserRealm userRealm(EhCacheManager cacheManager) {
109 UserRealm userRealm = new UserRealm(); 104 UserRealm userRealm = new UserRealm();
110 userRealm.setCacheManager(cacheManager); 105 userRealm.setCacheManager(cacheManager);
111 return userRealm; 106 return userRealm;
@@ -115,8 +110,7 @@ public class ShiroConfig @@ -115,8 +110,7 @@ public class ShiroConfig
115 * 自定义sessionDAO会话 110 * 自定义sessionDAO会话
116 */ 111 */
117 @Bean 112 @Bean
118 - public OnlineSessionDAO sessionDAO()  
119 - { 113 + public OnlineSessionDAO sessionDAO() {
120 OnlineSessionDAO sessionDAO = new OnlineSessionDAO(); 114 OnlineSessionDAO sessionDAO = new OnlineSessionDAO();
121 return sessionDAO; 115 return sessionDAO;
122 } 116 }
@@ -125,8 +119,7 @@ public class ShiroConfig @@ -125,8 +119,7 @@ public class ShiroConfig
125 * 自定义sessionFactory会话 119 * 自定义sessionFactory会话
126 */ 120 */
127 @Bean 121 @Bean
128 - public OnlineSessionFactory sessionFactory()  
129 - { 122 + public OnlineSessionFactory sessionFactory() {
130 OnlineSessionFactory sessionFactory = new OnlineSessionFactory(); 123 OnlineSessionFactory sessionFactory = new OnlineSessionFactory();
131 return sessionFactory; 124 return sessionFactory;
132 } 125 }
@@ -135,8 +128,7 @@ public class ShiroConfig @@ -135,8 +128,7 @@ public class ShiroConfig
135 * 自定义sessionFactory调度器 128 * 自定义sessionFactory调度器
136 */ 129 */
137 @Bean 130 @Bean
138 - public SpringSessionValidationScheduler sessionValidationScheduler()  
139 - { 131 + public SpringSessionValidationScheduler sessionValidationScheduler() {
140 SpringSessionValidationScheduler sessionValidationScheduler = new SpringSessionValidationScheduler(); 132 SpringSessionValidationScheduler sessionValidationScheduler = new SpringSessionValidationScheduler();
141 // 相隔多久检查一次session的有效性,单位毫秒,默认就是10分钟 133 // 相隔多久检查一次session的有效性,单位毫秒,默认就是10分钟
142 sessionValidationScheduler.setSessionValidationInterval(validationInterval * 60 * 1000); 134 sessionValidationScheduler.setSessionValidationInterval(validationInterval * 60 * 1000);
@@ -149,8 +141,7 @@ public class ShiroConfig @@ -149,8 +141,7 @@ public class ShiroConfig
149 * 会话管理器 141 * 会话管理器
150 */ 142 */
151 @Bean 143 @Bean
152 - public OnlineWebSessionManager sessionValidationManager()  
153 - { 144 + public OnlineWebSessionManager sessionValidationManager() {
154 OnlineWebSessionManager manager = new OnlineWebSessionManager(); 145 OnlineWebSessionManager manager = new OnlineWebSessionManager();
155 // 加入缓存管理器 146 // 加入缓存管理器
156 manager.setCacheManager(getEhCacheManager()); 147 manager.setCacheManager(getEhCacheManager());
@@ -173,8 +164,7 @@ public class ShiroConfig @@ -173,8 +164,7 @@ public class ShiroConfig
173 * 会话管理器 164 * 会话管理器
174 */ 165 */
175 @Bean 166 @Bean
176 - public OnlineWebSessionManager sessionManager()  
177 - { 167 + public OnlineWebSessionManager sessionManager() {
178 OnlineWebSessionManager manager = new OnlineWebSessionManager(); 168 OnlineWebSessionManager manager = new OnlineWebSessionManager();
179 // 加入缓存管理器 169 // 加入缓存管理器
180 manager.setCacheManager(getEhCacheManager()); 170 manager.setCacheManager(getEhCacheManager());
@@ -199,8 +189,7 @@ public class ShiroConfig @@ -199,8 +189,7 @@ public class ShiroConfig
199 * 安全管理器 189 * 安全管理器
200 */ 190 */
201 @Bean 191 @Bean
202 - public SecurityManager securityManager(UserRealm userRealm)  
203 - { 192 + public SecurityManager securityManager(UserRealm userRealm) {
204 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 193 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
205 // 设置realm. 194 // 设置realm.
206 securityManager.setRealm(userRealm); 195 securityManager.setRealm(userRealm);
@@ -216,15 +205,13 @@ public class ShiroConfig @@ -216,15 +205,13 @@ public class ShiroConfig
216 /** 205 /**
217 * 退出过滤器 206 * 退出过滤器
218 */ 207 */
219 - public LogoutFilter logoutFilter()  
220 - { 208 + public LogoutFilter logoutFilter() {
221 LogoutFilter logoutFilter = new LogoutFilter(); 209 LogoutFilter logoutFilter = new LogoutFilter();
222 logoutFilter.setLoginUrl(loginUrl); 210 logoutFilter.setLoginUrl(loginUrl);
223 return logoutFilter; 211 return logoutFilter;
224 } 212 }
225 213
226 - public LogoutFilter logoutFilters()  
227 - { 214 + public LogoutFilter logoutFilters() {
228 LogoutFilter logoutFilter = new LogoutFilter(); 215 LogoutFilter logoutFilter = new LogoutFilter();
229 logoutFilter.setLoginUrl(loginUrls); 216 logoutFilter.setLoginUrl(loginUrls);
230 return logoutFilter; 217 return logoutFilter;
@@ -234,8 +221,7 @@ public class ShiroConfig @@ -234,8 +221,7 @@ public class ShiroConfig
234 * Shiro过滤器配置 221 * Shiro过滤器配置
235 */ 222 */
236 @Bean 223 @Bean
237 - public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)  
238 - { 224 + public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
239 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 225 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
240 // Shiro的核心安全接口,这个属性是必须的 226 // Shiro的核心安全接口,这个属性是必须的
241 shiroFilterFactoryBean.setSecurityManager(securityManager); 227 shiroFilterFactoryBean.setSecurityManager(securityManager);
@@ -270,6 +256,7 @@ public class ShiroConfig @@ -270,6 +256,7 @@ public class ShiroConfig
270 filterChainDefinitionMap.put("/mobile/login", "anon"); 256 filterChainDefinitionMap.put("/mobile/login", "anon");
271 filterChainDefinitionMap.put("/getWarehouseByUserCode", "anon"); 257 filterChainDefinitionMap.put("/getWarehouseByUserCode", "anon");
272 filterChainDefinitionMap.put("/API/WMS/v2/login", "anon"); 258 filterChainDefinitionMap.put("/API/WMS/v2/login", "anon");
  259 + filterChainDefinitionMap.put("/api/**", "anon");
273 // 系统权限列表 260 // 系统权限列表
274 // filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll()); 261 // filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll());
275 262
@@ -292,8 +279,7 @@ public class ShiroConfig @@ -292,8 +279,7 @@ public class ShiroConfig
292 * 自定义在线用户处理过滤器 279 * 自定义在线用户处理过滤器
293 */ 280 */
294 @Bean 281 @Bean
295 - public OnlineSessionFilter onlineSessionFilter()  
296 - { 282 + public OnlineSessionFilter onlineSessionFilter() {
297 OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter(); 283 OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter();
298 onlineSessionFilter.setLoginUrl(loginUrl); 284 onlineSessionFilter.setLoginUrl(loginUrl);
299 return onlineSessionFilter; 285 return onlineSessionFilter;
@@ -303,8 +289,7 @@ public class ShiroConfig @@ -303,8 +289,7 @@ public class ShiroConfig
303 * 自定义在线用户同步过滤器 289 * 自定义在线用户同步过滤器
304 */ 290 */
305 @Bean 291 @Bean
306 - public SyncOnlineSessionFilter syncOnlineSessionFilter()  
307 - { 292 + public SyncOnlineSessionFilter syncOnlineSessionFilter() {
308 SyncOnlineSessionFilter syncOnlineSessionFilter = new SyncOnlineSessionFilter(); 293 SyncOnlineSessionFilter syncOnlineSessionFilter = new SyncOnlineSessionFilter();
309 return syncOnlineSessionFilter; 294 return syncOnlineSessionFilter;
310 } 295 }
@@ -312,8 +297,7 @@ public class ShiroConfig @@ -312,8 +297,7 @@ public class ShiroConfig
312 /** 297 /**
313 * cookie 属性设置 298 * cookie 属性设置
314 */ 299 */
315 - public SimpleCookie rememberMeCookie()  
316 - { 300 + public SimpleCookie rememberMeCookie() {
317 SimpleCookie cookie = new SimpleCookie("rememberMe"); 301 SimpleCookie cookie = new SimpleCookie("rememberMe");
318 cookie.setDomain(domain); 302 cookie.setDomain(domain);
319 cookie.setPath(path); 303 cookie.setPath(path);
@@ -325,8 +309,7 @@ public class ShiroConfig @@ -325,8 +309,7 @@ public class ShiroConfig
325 /** 309 /**
326 * 记住我 310 * 记住我
327 */ 311 */
328 - public CookieRememberMeManager rememberMeManager()  
329 - { 312 + public CookieRememberMeManager rememberMeManager() {
330 CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); 313 CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
331 cookieRememberMeManager.setCookie(rememberMeCookie()); 314 cookieRememberMeManager.setCookie(rememberMeCookie());
332 cookieRememberMeManager.setCipherKey(Base64.decode("fCq+/xW488hMTCD+cmJ3aQ==")); 315 cookieRememberMeManager.setCipherKey(Base64.decode("fCq+/xW488hMTCD+cmJ3aQ=="));
@@ -337,8 +320,7 @@ public class ShiroConfig @@ -337,8 +320,7 @@ public class ShiroConfig
337 * thymeleaf模板引擎和shiro框架的整合 320 * thymeleaf模板引擎和shiro框架的整合
338 */ 321 */
339 @Bean 322 @Bean
340 - public ShiroDialect shiroDialect()  
341 - { 323 + public ShiroDialect shiroDialect() {
342 return new ShiroDialect(); 324 return new ShiroDialect();
343 } 325 }
344 326
@@ -347,8 +329,7 @@ public class ShiroConfig @@ -347,8 +329,7 @@ public class ShiroConfig
347 */ 329 */
348 @Bean 330 @Bean
349 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor( 331 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
350 - @Qualifier("securityManager") SecurityManager securityManager)  
351 - { 332 + @Qualifier("securityManager") SecurityManager securityManager) {
352 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); 333 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
353 authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); 334 authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
354 return authorizationAttributeSourceAdvisor; 335 return authorizationAttributeSourceAdvisor;
src/main/java/com/huaheng/framework/redis/RedisCache.java 0 → 100644
  1 +package com.huaheng.framework.redis;
  2 +
  3 +import org.springframework.beans.factory.annotation.Autowired;
  4 +import org.springframework.data.redis.core.*;
  5 +import org.springframework.stereotype.Component;
  6 +
  7 +import java.util.*;
  8 +import java.util.concurrent.TimeUnit;
  9 +
  10 +/**
  11 + * spring redis 工具类
  12 + *
  13 + * @author ruoyi
  14 + **/
  15 +@SuppressWarnings(value = {"unchecked", "rawtypes"})
  16 +@Component
  17 +public class RedisCache {
  18 +
  19 + @Autowired
  20 + public RedisTemplate redisTemplate;
  21 +
  22 + /**
  23 + * 缓存基本的对象,Integer、String、实体类等
  24 + *
  25 + * @param key 缓存的键值
  26 + * @param value 缓存的值
  27 + * @return 缓存的对象
  28 + */
  29 + public <T> ValueOperations<String, T> setCacheObject(String key, T value) {
  30 + ValueOperations<String, T> operation = redisTemplate.opsForValue();
  31 + operation.set(key, value);
  32 + return operation;
  33 + }
  34 +
  35 + /**
  36 + * 缓存基本的对象,Integer、String、实体类等
  37 + *
  38 + * @param key 缓存的键值
  39 + * @param value 缓存的值
  40 + * @param timeout 时间
  41 + * @param timeUnit 时间颗粒度
  42 + * @return 缓存的对象
  43 + */
  44 + public <T> ValueOperations<String, T> setCacheObject(String key, T value, Integer timeout, TimeUnit timeUnit) {
  45 + ValueOperations<String, T> operation = redisTemplate.opsForValue();
  46 + operation.set(key, value, timeout, timeUnit);
  47 + return operation;
  48 + }
  49 +
  50 + /**
  51 + * 获得缓存的基本对象。
  52 + *
  53 + * @param key 缓存键值
  54 + * @return 缓存键值对应的数据
  55 + */
  56 + public <T> T getCacheObject(String key) {
  57 + ValueOperations<String, T> operation = redisTemplate.opsForValue();
  58 + return operation.get(key);
  59 + }
  60 +
  61 + /**
  62 + * 删除单个对象
  63 + *
  64 + * @param key
  65 + */
  66 + public void deleteObject(String key) {
  67 + redisTemplate.delete(key);
  68 + }
  69 +
  70 + /**
  71 + * 删除集合对象
  72 + *
  73 + * @param collection
  74 + */
  75 + public void deleteObject(Collection collection) {
  76 + redisTemplate.delete(collection);
  77 + }
  78 +
  79 + /**
  80 + * 缓存List数据
  81 + *
  82 + * @param key 缓存的键值
  83 + * @param dataList 待缓存的List数据
  84 + * @return 缓存的对象
  85 + */
  86 + public <T> ListOperations<String, T> setCacheList(String key, List<T> dataList) {
  87 + ListOperations listOperation = redisTemplate.opsForList();
  88 + if (null != dataList) {
  89 + int size = dataList.size();
  90 + for (int i = 0; i < size; i++) {
  91 + listOperation.leftPush(key, dataList.get(i));
  92 + }
  93 + }
  94 + return listOperation;
  95 + }
  96 +
  97 + /**
  98 + * 获得缓存的list对象
  99 + *
  100 + * @param key 缓存的键值
  101 + * @return 缓存键值对应的数据
  102 + */
  103 + public <T> List<T> getCacheList(String key) {
  104 + List<T> dataList = new ArrayList<T>();
  105 + ListOperations<String, T> listOperation = redisTemplate.opsForList();
  106 + Long size = listOperation.size(key);
  107 +
  108 + for (int i = 0; i < size; i++) {
  109 + dataList.add(listOperation.index(key, i));
  110 + }
  111 + return dataList;
  112 + }
  113 +
  114 + /**
  115 + * 缓存Set
  116 + *
  117 + * @param key 缓存键值
  118 + * @param dataSet 缓存的数据
  119 + * @return 缓存数据的对象
  120 + */
  121 + public <T> BoundSetOperations<String, T> setCacheSet(String key, Set<T> dataSet) {
  122 + BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
  123 + Iterator<T> it = dataSet.iterator();
  124 + while (it.hasNext()) {
  125 + setOperation.add(it.next());
  126 + }
  127 + return setOperation;
  128 + }
  129 +
  130 + /**
  131 + * 获得缓存的set
  132 + *
  133 + * @param key
  134 + * @return
  135 + */
  136 + public <T> Set<T> getCacheSet(String key) {
  137 + Set<T> dataSet = new HashSet<T>();
  138 + BoundSetOperations<String, T> operation = redisTemplate.boundSetOps(key);
  139 + dataSet = operation.members();
  140 + return dataSet;
  141 + }
  142 +
  143 + /**
  144 + * 缓存Map
  145 + *
  146 + * @param key
  147 + * @param dataMap
  148 + * @return
  149 + */
  150 + public <T> HashOperations<String, String, T> setCacheMap(String key, Map<String, T> dataMap) {
  151 + HashOperations hashOperations = redisTemplate.opsForHash();
  152 + if (null != dataMap) {
  153 + for (Map.Entry<String, T> entry : dataMap.entrySet()) {
  154 + hashOperations.put(key, entry.getKey(), entry.getValue());
  155 + }
  156 + }
  157 + return hashOperations;
  158 + }
  159 +
  160 + /**
  161 + * 获得缓存的Map
  162 + *
  163 + * @param key
  164 + * @return
  165 + */
  166 + public <T> Map<String, T> getCacheMap(String key) {
  167 + Map<String, T> map = redisTemplate.opsForHash().entries(key);
  168 + return map;
  169 + }
  170 +
  171 + /**
  172 + * 获得缓存的基本对象列表
  173 + *
  174 + * @param pattern 字符串前缀
  175 + * @return 对象列表
  176 + */
  177 + public Collection<String> keys(String pattern) {
  178 + return redisTemplate.keys(pattern);
  179 + }
  180 +}
src/main/java/com/huaheng/framework/token/ApiInterceptor.java 0 → 100644
  1 +package com.huaheng.framework.token;
  2 +
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.huaheng.common.utils.ServletUtils;
  5 +import com.huaheng.common.utils.StringUtils;
  6 +import com.huaheng.framework.redis.RedisCache;
  7 +import org.springframework.web.servlet.HandlerInterceptor;
  8 +
  9 +import javax.annotation.Resource;
  10 +import javax.servlet.http.HttpServletRequest;
  11 +import javax.servlet.http.HttpServletResponse;
  12 +import java.security.SignatureException;
  13 +
  14 +/**
  15 + * Created by Enzo Cotter on 2020/6/11.
  16 + */
  17 +public class ApiInterceptor implements HandlerInterceptor {
  18 +
  19 + @Resource
  20 + private RedisCache redisCache;
  21 + /**
  22 + * 可以在这里设置各种规则,取到token后解析,来验证token有效性,有效期等等。这里仅仅验证了是不是token为空。
  23 + */
  24 + @Override
  25 + public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
  26 + //这个就是从http头中取约定好的token的key。
  27 + String token = httpServletRequest.getHeader("Authorization");
  28 + try {
  29 + if (token == null || token.trim().equals("")) {
  30 + throw new SignatureException("token is null");
  31 + } else {
  32 + token = token.substring(7);
  33 + String user = redisCache.getCacheObject(token);
  34 + if (StringUtils.isEmpty(user)) {
  35 + JSONObject jsonObject = new JSONObject();
  36 + jsonObject.put("msg", "token不正确或过期");
  37 + jsonObject.put("code", 401);
  38 + ServletUtils.renderString(httpServletResponse, jsonObject.toString());
  39 + return false;
  40 + }
  41 + }
  42 + } catch (SignatureException e) {
  43 + JSONObject jsonObject = new JSONObject();
  44 + jsonObject.put("msg", "请求参数中找不到Token");
  45 + jsonObject.put("code", 401);
  46 + ServletUtils.renderString(httpServletResponse, jsonObject.toString());
  47 + return false;
  48 + }
  49 +
  50 + return true;
  51 + }
  52 +}
src/main/java/com/huaheng/framework/token/TokenController.java 0 → 100644
  1 +package com.huaheng.framework.token;
  2 +
  3 +import com.huaheng.common.utils.StringUtils;
  4 +import com.huaheng.framework.shiro.service.PasswordService;
  5 +import com.huaheng.framework.web.controller.BaseController;
  6 +import com.huaheng.framework.web.domain.Result;
  7 +import com.huaheng.pc.system.user.domain.User;
  8 +import com.huaheng.pc.system.user.service.IUserService;
  9 +import org.springframework.web.bind.annotation.PostMapping;
  10 +import org.springframework.web.bind.annotation.RequestMapping;
  11 +import org.springframework.web.bind.annotation.ResponseBody;
  12 +import org.springframework.web.bind.annotation.RestController;
  13 +
  14 +import javax.annotation.Resource;
  15 +import java.util.Calendar;
  16 +
  17 +/**
  18 + * Created by Enzo Cotter on 2020/6/11.
  19 + */
  20 +@RestController
  21 +@RequestMapping("/api")
  22 +public class TokenController extends BaseController {
  23 +
  24 + @Resource
  25 + private TokenService tokenService;
  26 + @Resource
  27 + private IUserService userService;
  28 + @Resource
  29 + private PasswordService passwordService;
  30 +
  31 + @PostMapping("/getToken")
  32 + @ResponseBody
  33 + public Result getToken(String username, String password, String warehouseCode) {
  34 + if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
  35 + return Result.error("用户名和密码不能为空");
  36 + }
  37 + if (StringUtils.isEmpty(warehouseCode)) {
  38 + return Result.error("请选择仓库");
  39 + }
  40 + User user = userService.selectUserByLoginName(username);
  41 +
  42 + if (!userService.checkWarehouseCodeAndUserName(warehouseCode, username)) {
  43 + return Result.error("用户没有该仓库操作权限");
  44 + }
  45 + if (user.getPassword().equals(passwordService.encryptPassword(user.getLoginName(), password, user.getSalt()))) {
  46 + String token = tokenService.createToken(user);
  47 + Result ajaxResult = Result.success("成功");
  48 + ajaxResult.put("token", token);
  49 + ajaxResult.put("expireTime", Calendar.getInstance().getTime());
  50 + return ajaxResult;
  51 + } else {
  52 + return Result.error("密码错误");
  53 + }
  54 +
  55 + }
  56 +}
src/main/java/com/huaheng/framework/token/TokenService.java 0 → 100644
  1 +package com.huaheng.framework.token;
  2 +
  3 +import com.alibaba.fastjson.JSONArray;
  4 +import com.alibaba.fastjson.JSONObject;
  5 +import com.fasterxml.jackson.annotation.JsonFormat;
  6 +import com.google.gson.JsonObject;
  7 +import com.huaheng.common.exception.service.ServiceException;
  8 +import com.huaheng.framework.redis.RedisCache;
  9 +import com.huaheng.pc.system.user.domain.User;
  10 +import io.jsonwebtoken.Claims;
  11 +import io.jsonwebtoken.Jws;
  12 +import io.jsonwebtoken.Jwts;
  13 +import io.jsonwebtoken.SignatureAlgorithm;
  14 +import jdk.nashorn.internal.parser.JSONParser;
  15 +import org.springframework.boot.autoconfigure.cache.CacheProperties;
  16 +import org.springframework.stereotype.Service;
  17 +
  18 +import javax.annotation.Resource;
  19 +import java.util.Calendar;
  20 +import java.util.Date;
  21 +import java.util.Map;
  22 +import java.util.concurrent.TimeUnit;
  23 +
  24 +/**
  25 + * token生成与解析
  26 + * @author Enzo Cotter
  27 + * @date 2020/6/11
  28 + */
  29 +@Service
  30 +public class TokenService {
  31 +
  32 + @Resource
  33 + private RedisCache redisCache;
  34 +
  35 + /**
  36 + * 有效期7天
  37 + */
  38 + private static final int EXPIRE_TIME = 7;
  39 +
  40 + /**
  41 + * 盐
  42 + */
  43 + private static final String signingKey = "secret";
  44 +
  45 + /**
  46 + * 创建token
  47 + * @param user 用户
  48 + * @return
  49 + */
  50 + public String createToken(User user){
  51 + //签发时间
  52 + Date iatTime = new Date();
  53 + //expire time
  54 + Calendar nowTime = Calendar.getInstance();
  55 + nowTime.add(Calendar.DATE, EXPIRE_TIME);
  56 + Date expireTime = nowTime.getTime();
  57 +
  58 + Claims claims = Jwts.claims();
  59 + claims.put("user", user);
  60 + claims.setIssuedAt(iatTime);
  61 + String token = Jwts.builder().setClaims(claims)
  62 + .signWith(SignatureAlgorithm.HS512,signingKey).compact();
  63 + if (redisCache.setCacheObject(token, user.toString(), EXPIRE_TIME, TimeUnit.DAYS) == null) {
  64 + throw new ServiceException("redis异常");
  65 + }
  66 + return token;
  67 + }
  68 +
  69 + /**
  70 + * 解析token
  71 + * @param token
  72 + */
  73 + public void parseToken(String token){
  74 + Jws<Claims> jws = Jwts.parser().setSigningKey(signingKey).parseClaimsJws(token);
  75 + Claims claims = jws.getBody();
  76 + Map<String,String> header = jws.getHeader();
  77 + System.out.println("parse");
  78 + }
  79 +}
src/main/java/com/huaheng/framework/token/WebAppConfigurer.java 0 → 100644
  1 +package com.huaheng.framework.token;
  2 +
  3 +import org.springframework.context.annotation.Bean;
  4 +import org.springframework.context.annotation.Configuration;
  5 +import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  6 +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  7 +
  8 +/**
  9 + * Created by Enzo Cotter on 2020/6/11.
  10 + */
  11 +@Configuration
  12 +public class WebAppConfigurer implements WebMvcConfigurer {
  13 +
  14 + @Bean
  15 + ApiInterceptor apiInterceptor(){return new ApiInterceptor();}
  16 +
  17 + @Override
  18 + public void addInterceptors(InterceptorRegistry interceptorRegistry) {
  19 + interceptorRegistry.addInterceptor(apiInterceptor())
  20 + .addPathPatterns("/api/**")
  21 + .excludePathPatterns("/api/getToken");
  22 + }
  23 +}
src/main/java/com/huaheng/framework/web/domain/Result.java 0 → 100644
  1 +package com.huaheng.framework.web.domain;
  2 +
  3 +import com.huaheng.common.utils.StringUtils;
  4 +
  5 +import java.util.HashMap;
  6 +
  7 +/**
  8 + * 操作消息提醒
  9 + * @author Enzo Cotter
  10 + * @date 2020/6/11
  11 + */
  12 +public class Result extends HashMap<String, Object> {
  13 + private static final long serialVersionUID = 1L;
  14 +
  15 + /**
  16 + * 状态码
  17 + */
  18 + public static final String CODE_TAG = "code";
  19 +
  20 + /**
  21 + * 返回内容
  22 + */
  23 + public static final String MSG_TAG = "msg";
  24 +
  25 + /**
  26 + * 数据对象
  27 + */
  28 + public static final String DATA_TAG = "data";
  29 +
  30 + /**
  31 + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
  32 + */
  33 + public Result() {
  34 + }
  35 +
  36 + /**
  37 + * 初始化一个新创建的 AjaxResult 对象
  38 + *
  39 + * @param code 状态码
  40 + * @param msg 返回内容
  41 + */
  42 + public Result(int code, String msg) {
  43 + super.put(CODE_TAG, code);
  44 + super.put(MSG_TAG, msg);
  45 + }
  46 +
  47 + /**
  48 + * 初始化一个新创建的 AjaxResult 对象
  49 + *
  50 + * @param code 状态码
  51 + * @param msg 返回内容
  52 + * @param data 数据对象
  53 + */
  54 + public Result(int code, String msg, Object data) {
  55 + super.put(CODE_TAG, code);
  56 + super.put(MSG_TAG, msg);
  57 + if (StringUtils.isNotNull(data)) {
  58 + super.put(DATA_TAG, data);
  59 + }
  60 + }
  61 +
  62 + /**
  63 + * 返回成功消息
  64 + *
  65 + * @return 成功消息
  66 + */
  67 + public static Result success() {
  68 + return Result.success("操作成功");
  69 + }
  70 +
  71 + /**
  72 + * 返回成功数据
  73 + *
  74 + * @return 成功消息
  75 + */
  76 + public static Result success(Object data) {
  77 + return Result.success("操作成功", data);
  78 + }
  79 +
  80 + /**
  81 + * 返回成功消息
  82 + *
  83 + * @param msg 返回内容
  84 + * @return 成功消息
  85 + */
  86 + public static Result success(String msg) {
  87 + return Result.success(msg, null);
  88 + }
  89 +
  90 + /**
  91 + * 返回成功消息
  92 + *
  93 + * @param msg 返回内容
  94 + * @param data 数据对象
  95 + * @return 成功消息
  96 + */
  97 + public static Result success(String msg, Object data) {
  98 + return new Result(200, msg, data);
  99 + }
  100 +
  101 + /**
  102 + * 返回错误消息
  103 + *
  104 + * @return
  105 + */
  106 + public static Result error() {
  107 + return Result.error("操作失败");
  108 + }
  109 +
  110 + /**
  111 + * 返回错误消息
  112 + *
  113 + * @param msg 返回内容
  114 + * @return 警告消息
  115 + */
  116 + public static Result error(String msg) {
  117 + return Result.error(msg, null);
  118 + }
  119 +
  120 + /**
  121 + * 返回错误消息
  122 + *
  123 + * @param msg 返回内容
  124 + * @param data 数据对象
  125 + * @return 警告消息
  126 + */
  127 + public static Result error(String msg, Object data) {
  128 + return new Result(400, msg, data);
  129 + }
  130 +
  131 + /**
  132 + * 返回错误消息
  133 + *
  134 + * @param code 状态码
  135 + * @param msg 返回内容
  136 + * @return 警告消息
  137 + */
  138 + public static Result error(int code, String msg) {
  139 + return new Result(code, msg, null);
  140 + }
  141 +
  142 +}
src/main/java/com/huaheng/pc/receipt/receiving/controller/ReceivingController.java
@@ -98,7 +98,7 @@ public class ReceivingController extends BaseController { @@ -98,7 +98,7 @@ public class ReceivingController extends BaseController {
98 // if (count > 0) { 98 // if (count > 0) {
99 // return AjaxResult.error("仓库有“出库查看”任务没有完成,请先完成任务"); 99 // return AjaxResult.error("仓库有“出库查看”任务没有完成,请先完成任务");
100 // } 100 // }
101 - AjaxResult result = receiptContainerHeaderService.saveCountain(receiptCode, containerCode, receiptDetailId, 101 + AjaxResult result = receiptContainerHeaderService.saveContainer(receiptCode, containerCode, receiptDetailId,
102 locationCode, qty, locatingRule); 102 locationCode, qty, locatingRule);
103 return result; 103 return result;
104 } 104 }
src/main/java/com/huaheng/pc/system/user/mapper/UserMapper.java
@@ -119,6 +119,14 @@ public interface UserMapper @@ -119,6 +119,14 @@ public interface UserMapper
119 public User checkEmailUnique(String email); 119 public User checkEmailUnique(String email);
120 120
121 /** 121 /**
  122 + * 检查用户是否拥有该仓库权限
  123 + * @param warehouseCode 仓库code
  124 + * @param username 登录名
  125 + * @return
  126 + */
  127 + List<Map<String, Object>> checkWarehouseCodeAndUserName(@Param("warehouseCode") String warehouseCode, @Param("loginName") String username);
  128 +
  129 + /**
122 * 根据用户id查询该用户所有仓库 130 * 根据用户id查询该用户所有仓库
123 * @param userId 131 * @param userId
124 * @return 132 * @return
src/main/java/com/huaheng/pc/system/user/service/IUserService.java
@@ -200,6 +200,8 @@ public interface IUserService @@ -200,6 +200,8 @@ public interface IUserService
200 public User selectmen(String loginName); 200 public User selectmen(String loginName);
201 201
202 public int batchUserWarehouse(@Param("userWarehouseList") List<SysUserWarehouse> userWarehouseList); 202 public int batchUserWarehouse(@Param("userWarehouseList") List<SysUserWarehouse> userWarehouseList);
  203 +
  204 + boolean checkWarehouseCodeAndUserName(String warehouseCode, String username);
203 } 205 }
204 206
205 207
src/main/java/com/huaheng/pc/system/user/service/UserServiceImpl.java
@@ -594,4 +594,9 @@ public class UserServiceImpl implements IUserService @@ -594,4 +594,9 @@ public class UserServiceImpl implements IUserService
594 return sysUserWarehouseMapper.batchUserWarehouse(userWarehouseList); 594 return sysUserWarehouseMapper.batchUserWarehouse(userWarehouseList);
595 } 595 }
596 596
  597 + @Override
  598 + public boolean checkWarehouseCodeAndUserName(String warehouseCode, String username) {
  599 + return !userMapper.checkWarehouseCodeAndUserName(warehouseCode, username).isEmpty();
  600 + }
  601 +
597 } 602 }
src/main/resources/application.yml
@@ -84,6 +84,26 @@ spring: @@ -84,6 +84,26 @@ spring:
84 password: owobzjvlgsxrbdfe 84 password: owobzjvlgsxrbdfe
85 # 编码类型 85 # 编码类型
86 default-encoding: utf-8 86 default-encoding: utf-8
  87 + # redis 配置
  88 + redis:
  89 + # 地址
  90 + host: localhost
  91 + # 端口,默认为6379
  92 + port: 6379
  93 + # 密码
  94 + password:
  95 + # 连接超时时间
  96 + timeout: 10s
  97 + lettuce:
  98 + pool:
  99 + # 连接池中的最小空闲连接
  100 + min-idle: 0
  101 + # 连接池中的最大空闲连接
  102 + max-idle: 8
  103 + # 连接池的最大数据库连接数
  104 + max-active: 8
  105 + # #连接池最大阻塞等待时间(使用负值表示没有限制)
  106 + max-wait: -1ms
87 107
88 mybatis-plus: 108 mybatis-plus:
89 mapper-locations: classpath:mybatis/**/*.xml 109 mapper-locations: classpath:mybatis/**/*.xml
src/main/resources/mybatis/system/UserMapper.xml
@@ -203,5 +203,12 @@ @@ -203,5 +203,12 @@
203 <update id="insertupdateTime" > 203 <update id="insertupdateTime" >
204 update sys_user set updateTime = #{date} where loginName = #{cPersonCode} 204 update sys_user set updateTime = #{date} where loginName = #{cPersonCode}
205 </update> 205 </update>
  206 + <select id="checkWarehouseCodeAndUserName" resultType="java.util.Map">
  207 + SELECT r.`name`, r.code
  208 + FROM sys_user u
  209 + LEFT JOIN sys_user_warehouse ur ON u.id = ur.userId
  210 + LEFT JOIN warehouse r ON ur.warehouseCode = r.code
  211 + WHERE u.loginName=#{loginName,jdbcType=VARCHAR} AND r.`code` =#{warehouseCode,jdbcType=VARCHAR}
  212 + </select>
206 213
207 </mapper> 214 </mapper>
208 \ No newline at end of file 215 \ No newline at end of file
src/main/resources/templates/config/container/print.html
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 <head> 4 <head>
5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
6 <head th:include="include :: header"></head> 6 <head th:include="include :: header"></head>
7 - <title>库存交易明细</title> 7 + <title>容器打印</title>
8 <!--<link href="css/bootstrap.min.css" rel="stylesheet" />--> 8 <!--<link href="css/bootstrap.min.css" rel="stylesheet" />-->
9 <!--<link href="css/font-awesome.min.css" rel="stylesheet" />--> 9 <!--<link href="css/font-awesome.min.css" rel="stylesheet" />-->
10 <!--&lt;!&ndash;[if IE]>--> 10 <!--&lt;!&ndash;[if IE]>-->