sunlightcs 7 năm trước cách đây
mục cha
commit
afd9e2074e
52 tập tin đã thay đổi với 770 bổ sung498 xóa
  1. 1 1
      Dockerfile
  2. 1 8
      doc/db.sql
  3. 7 1
      pom-war.xml
  4. 13 0
      pom.xml
  5. 88 0
      src/main/java/io/renren/common/utils/JwtUtils.java
  6. 1 0
      src/main/java/io/renren/common/utils/R.java
  7. 10 0
      src/main/java/io/renren/common/utils/ShiroUtils.java
  8. 32 0
      src/main/java/io/renren/config/KaptchaConfig.java
  9. 3 2
      src/main/java/io/renren/config/ShiroConfig.java
  10. 14 0
      src/main/java/io/renren/datasources/DataSourceNames.java
  11. 28 0
      src/main/java/io/renren/datasources/DataSourceTestService.java
  12. 1 1
      src/main/java/io/renren/dynamicdatasource/DynamicDataSource.java
  13. 3 3
      src/main/java/io/renren/dynamicdatasource/DynamicDataSourceConfig.java
  14. 16 0
      src/main/java/io/renren/datasources/annotation/DataSource.java
  15. 60 0
      src/main/java/io/renren/datasources/aspect/DataSourceAspect.java
  16. 0 23
      src/main/java/io/renren/dynamicdatasource/DataSourceContext.java
  17. 0 46
      src/main/java/io/renren/modules/api/controller/ApiTestController.java
  18. 0 21
      src/main/java/io/renren/modules/api/dao/TokenDao.java
  19. 0 75
      src/main/java/io/renren/modules/api/entity/TokenEntity.java
  20. 0 32
      src/main/java/io/renren/modules/api/service/TokenService.java
  21. 0 80
      src/main/java/io/renren/modules/api/service/impl/TokenServiceImpl.java
  22. 4 5
      src/main/java/io/renren/modules/api/annotation/AuthIgnore.java
  23. 1 1
      src/main/java/io/renren/modules/api/annotation/LoginUser.java
  24. 4 4
      src/main/java/io/renren/modules/api/config/WebMvcConfig.java
  25. 12 9
      src/main/java/io/renren/modules/api/controller/ApiLoginController.java
  26. 3 5
      src/main/java/io/renren/modules/api/controller/ApiRegisterController.java
  27. 50 0
      src/main/java/io/renren/modules/app/controller/ApiTestController.java
  28. 2 2
      src/main/java/io/renren/modules/api/dao/UserDao.java
  29. 1 1
      src/main/java/io/renren/modules/api/entity/UserEntity.java
  30. 19 21
      src/main/java/io/renren/modules/api/interceptor/AuthorizationInterceptor.java
  31. 6 6
      src/main/java/io/renren/modules/api/resolver/LoginUserHandlerMethodArgumentResolver.java
  32. 2 2
      src/main/java/io/renren/modules/api/service/UserService.java
  33. 4 4
      src/main/java/io/renren/modules/api/service/impl/UserServiceImpl.java
  34. 1 1
      src/main/java/io/renren/modules/job/utils/ScheduleUtils.java
  35. 5 5
      src/main/java/io/renren/modules/oss/cloud/AliyunCloudStorageService.java
  36. 9 6
      src/main/java/io/renren/modules/oss/cloud/CloudStorageService.java
  37. 20 20
      src/main/java/io/renren/modules/oss/cloud/QcloudCloudStorageService.java
  38. 5 5
      src/main/java/io/renren/modules/oss/cloud/QiniuCloudStorageService.java
  39. 7 7
      src/main/java/io/renren/modules/oss/controller/SysOssController.java
  40. 40 1
      src/main/java/io/renren/modules/sys/controller/SysLoginController.java
  41. 0 4
      src/main/java/io/renren/modules/sys/controller/SysMenuController.java
  42. 2 2
      src/main/java/io/renren/modules/sys/service/impl/SysConfigServiceImpl.java
  43. 1 1
      src/main/resources/application-dev.yml
  44. 10 1
      src/main/resources/application.yml
  45. 0 41
      src/main/resources/mapper/api/TokenDao.xml
  46. 4 4
      src/main/resources/mapper/api/UserDao.xml
  47. 1 1
      src/main/resources/static/js/modules/sys/menu.js
  48. 1 1
      src/main/resources/static/js/modules/sys/role.js
  49. 191 0
      src/main/resources/static/libs/store.js
  50. 50 36
      src/main/resources/views/login.html
  51. 13 9
      src/test/java/io/renren/DynamicDataSourceTest.java
  52. 24 0
      src/test/java/io/renren/JwtTest.java

+ 1 - 1
Dockerfile

@@ -2,6 +2,6 @@ FROM java:8
 EXPOSE 8080
 
 VOLUME /tmp
-ADD renren-fast-1.2.0.jar /app.jar
+ADD renren-fast.jar /app.jar
 RUN bash -c 'touch /app.jar'
 ENTRYPOINT ["java","-jar","/app.jar"]

+ 1 - 8
doc/db.sql

@@ -128,7 +128,7 @@ INSERT INTO `sys_menu` (`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`,
 
 
 -- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- APP接口相关SQL,如果不使用该功能,则不用执行下面SQL -------------------------------------------------------------------------------------------------------------
 -- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
 -- 用户表
@@ -142,15 +142,6 @@ CREATE TABLE `tb_user` (
   UNIQUE INDEX (`username`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户';
 
-CREATE TABLE `tb_token` (
-  `user_id` bigint NOT NULL,
-  `token` varchar(100) NOT NULL COMMENT 'token',
-  `expire_time` datetime COMMENT '过期时间',
-  `update_time` datetime COMMENT '更新时间',
-  PRIMARY KEY (`user_id`),
-  UNIQUE INDEX (`token`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户Token';
 
 -- 账号:13612345678  密码:admin
 INSERT INTO `tb_user` (`username`, `mobile`, `password`, `create_time`) VALUES ('mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', '2017-03-23 22:37:41');

+ 7 - 1
pom-war.xml

@@ -20,7 +20,7 @@
         <java.version>1.8</java.version>
         <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
         <mysql.version>5.1.38</mysql.version>
-        <druid.version>1.1.2</druid.version>
+        <druid.version>1.1.3</druid.version>
         <quartz.version>2.3.0</quartz.version>
         <commons.lang.version>2.6</commons.lang.version>
         <commons.fileupload.version>1.3.1</commons.fileupload.version>
@@ -28,6 +28,7 @@
         <commons.codec.version>1.10</commons.codec.version>
         <commons.configuration.version>1.10</commons.configuration.version>
         <shiro.version>1.3.2</shiro.version>
+        <jwt.version>0.7.0</jwt.version>
         <kaptcha.version>0.0.9</kaptcha.version>
         <qiniu.version>[7.2.0, 7.2.99]</qiniu.version>
         <aliyun.oss.version>2.5.0</aliyun.oss.version>
@@ -133,6 +134,11 @@
             <version>${shiro.version}</version>
         </dependency>
         <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>${jwt.version}</version>
+        </dependency>
+        <dependency>
             <groupId>com.github.axet</groupId>
             <artifactId>kaptcha</artifactId>
             <version>${kaptcha.version}</version>

+ 13 - 0
pom.xml

@@ -28,6 +28,8 @@
 		<commons.codec.version>1.10</commons.codec.version>
 		<commons.configuration.version>1.10</commons.configuration.version>
 		<shiro.version>1.3.2</shiro.version>
+		<jwt.version>0.7.0</jwt.version>
+		<kaptcha.version>0.0.9</kaptcha.version>
 		<qiniu.version>[7.2.0, 7.2.99]</qiniu.version>
 		<aliyun.oss.version>2.5.0</aliyun.oss.version>
 		<qcloud.cos.version>4.4</qcloud.cos.version>
@@ -134,6 +136,16 @@
 			<version>${shiro.version}</version>
 		</dependency>
 		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt</artifactId>
+			<version>${jwt.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.github.axet</groupId>
+			<artifactId>kaptcha</artifactId>
+			<version>${kaptcha.version}</version>
+		</dependency>
+		<dependency>
 			<groupId>com.qiniu</groupId>
 			<artifactId>qiniu-java-sdk</artifactId>
 			<version>${qiniu.version}</version>
@@ -157,6 +169,7 @@
 	</dependencies>
 
 	<build>
+		<finalName>${artifactId}</finalName>
 		<extensions>
 			<extension>
 				<groupId>org.apache.maven.wagon</groupId>

+ 88 - 0
src/main/java/io/renren/common/utils/JwtUtils.java

@@ -0,0 +1,88 @@
+package io.renren.common.utils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * jwt工具类
+ * @author chenshun
+ * @email sunlightcs@gmail.com
+ * @date 2017/9/21 22:21
+ */
+@ConfigurationProperties(prefix = "renren.jwt")
+@Component
+public class JwtUtils {
+    private Logger logger = LoggerFactory.getLogger(getClass());
+
+    private String secret;
+    private long expire;
+    private String header;
+
+    /**
+     * 生成jwt token
+     */
+    public String generateToken(long userId) {
+        Date nowDate = new Date();
+        //过期时间
+        Date expireDate = new Date(nowDate.getTime() + expire * 1000);
+
+        return Jwts.builder()
+                .setHeaderParam("typ", "JWT")
+                .setSubject(userId+"")
+                .setIssuedAt(nowDate)
+                .setExpiration(expireDate)
+                .signWith(SignatureAlgorithm.HS512, secret)
+                .compact();
+    }
+
+    public Claims getClaimByToken(String token) {
+        try {
+            return Jwts.parser()
+                    .setSigningKey(secret)
+                    .parseClaimsJws(token)
+                    .getBody();
+        }catch (Exception e){
+            logger.debug("validate is token error ", e);
+            return null;
+        }
+    }
+
+    /**
+     * token是否过期
+     * @return  true:过期
+     */
+    public boolean isTokenExpired(Date expiration) {
+        return expiration.before(new Date());
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    public void setSecret(String secret) {
+        this.secret = secret;
+    }
+
+    public long getExpire() {
+        return expire;
+    }
+
+    public void setExpire(long expire) {
+        this.expire = expire;
+    }
+
+    public String getHeader() {
+        return header;
+    }
+
+    public void setHeader(String header) {
+        this.header = header;
+    }
+}

+ 1 - 0
src/main/java/io/renren/common/utils/R.java

@@ -17,6 +17,7 @@ public class R extends HashMap<String, Object> {
 	
 	public R() {
 		put("code", 0);
+		put("msg", "success");
 	}
 	
 	public static R error() {

+ 10 - 0
src/main/java/io/renren/common/utils/ShiroUtils.java

@@ -1,5 +1,6 @@
 package io.renren.common.utils;
 
+import io.renren.common.exception.RRException;
 import io.renren.modules.sys.entity.SysUserEntity;
 import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.session.Session;
@@ -42,4 +43,13 @@ public class ShiroUtils {
 		return SecurityUtils.getSubject().getPrincipal() != null;
 	}
 
+	public static String getKaptcha(String key) {
+		Object kaptcha = getSessionAttribute(key);
+		if(kaptcha == null){
+			throw new RRException("验证码已失效");
+		}
+		getSession().removeAttribute(key);
+		return kaptcha.toString();
+	}
+
 }

+ 32 - 0
src/main/java/io/renren/config/KaptchaConfig.java

@@ -0,0 +1,32 @@
+package io.renren.config;
+
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Properties;
+
+
+/**
+ * 生成验证码配置
+ *
+ * @author chenshun
+ * @email sunlightcs@gmail.com
+ * @date 2017-04-20 19:22
+ */
+@Configuration
+public class KaptchaConfig {
+
+    @Bean
+    public DefaultKaptcha producer() {
+        Properties properties = new Properties();
+        properties.put("kaptcha.border", "no");
+        properties.put("kaptcha.textproducer.font.color", "black");
+        properties.put("kaptcha.textproducer.char.space", "5");
+        Config config = new Config(properties);
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+}

+ 3 - 2
src/main/java/io/renren/config/ShiroConfig.java

@@ -32,7 +32,7 @@ public class ShiroConfig {
     public SessionManager sessionManager(){
         DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
         sessionManager.setSessionValidationSchedulerEnabled(true);
-        sessionManager.setSessionIdCookieEnabled(false);
+        sessionManager.setSessionIdCookieEnabled(true);
         return sessionManager;
     }
 
@@ -58,7 +58,7 @@ public class ShiroConfig {
         Map<String, String> filterMap = new LinkedHashMap<>();
         filterMap.put("/webjars/**", "anon");
         filterMap.put("/druid/**", "anon");
-        filterMap.put("/api/**", "anon");
+        filterMap.put("/app/**", "anon");
         filterMap.put("/sys/login", "anon");
         filterMap.put("/**/*.css", "anon");
         filterMap.put("/**/*.js", "anon");
@@ -67,6 +67,7 @@ public class ShiroConfig {
         filterMap.put("/plugins/**", "anon");
         filterMap.put("/swagger/**", "anon");
         filterMap.put("/favicon.ico", "anon");
+        filterMap.put("/captcha.jpg", "anon");
         filterMap.put("/", "anon");
         filterMap.put("/**", "oauth2");
         shiroFilter.setFilterChainDefinitionMap(filterMap);

+ 14 - 0
src/main/java/io/renren/datasources/DataSourceNames.java

@@ -0,0 +1,14 @@
+package io.renren.datasources;
+
+/**
+ * 增加多数据源,在此配置
+ *
+ * @author chenshun
+ * @email sunlightcs@gmail.com
+ * @date 2017/8/18 23:46
+ */
+public interface DataSourceNames {
+    String FIRST = "first";
+    String SECOND = "second";
+
+}

+ 28 - 0
src/main/java/io/renren/datasources/DataSourceTestService.java

@@ -0,0 +1,28 @@
+package io.renren.datasources;
+
+import io.renren.datasources.annotation.DataSource;
+import io.renren.modules.app.entity.UserEntity;
+import io.renren.modules.app.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 测试
+ * @author chenshun
+ * @email sunlightcs@gmail.com
+ * @date 2017/9/16 23:10
+ */
+@Service
+public class DataSourceTestService {
+    @Autowired
+    private UserService userService;
+
+    public UserEntity queryObject(Long userId){
+        return userService.queryObject(userId);
+    }
+
+    @DataSource(name = DataSourceNames.SECOND)
+    public UserEntity queryObject2(Long userId){
+        return userService.queryObject(userId);
+    }
+}

+ 1 - 1
src/main/java/io/renren/dynamicdatasource/DynamicDataSource.java

@@ -1,4 +1,4 @@
-package io.renren.dynamicdatasource;
+package io.renren.datasources;
 
 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 

+ 3 - 3
src/main/java/io/renren/dynamicdatasource/DynamicDataSourceConfig.java

@@ -1,4 +1,4 @@
-package io.renren.dynamicdatasource;
+package io.renren.datasources;
 
 import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
 import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -35,8 +35,8 @@ public class DynamicDataSourceConfig {
     @Primary
     public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
         Map<String, DataSource> targetDataSources = new HashMap<>();
-        targetDataSources.put(DataSourceContext.FIRST.getName(), firstDataSource);
-        targetDataSources.put(DataSourceContext.SECOND.getName(), secondDataSource);
+        targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
+        targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
         return new DynamicDataSource(firstDataSource, targetDataSources);
     }
 }

+ 16 - 0
src/main/java/io/renren/datasources/annotation/DataSource.java

@@ -0,0 +1,16 @@
+package io.renren.datasources.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 多数据源注解
+ * @author chenshun
+ * @email sunlightcs@gmail.com
+ * @date 2017/9/16 22:16
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataSource {
+    String name() default "";
+}

+ 60 - 0
src/main/java/io/renren/datasources/aspect/DataSourceAspect.java

@@ -0,0 +1,60 @@
+package io.renren.datasources.aspect;
+
+import io.renren.datasources.DataSourceNames;
+import io.renren.datasources.DynamicDataSource;
+import io.renren.datasources.annotation.DataSource;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.Ordered;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+/**
+ * 多数据源,切面处理类
+ * @author chenshun
+ * @email sunlightcs@gmail.com
+ * @date 2017/9/16 22:20
+ */
+@Aspect
+@Component
+public class DataSourceAspect implements Ordered {
+    protected Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Pointcut("@annotation(io.renren.datasources.annotation.DataSource)")
+    public void dataSourcePointCut() {
+
+    }
+
+    @Around("dataSourcePointCut()")
+    public Object around(ProceedingJoinPoint point) throws Throwable {
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        Method method = signature.getMethod();
+
+        DataSource ds = method.getAnnotation(DataSource.class);
+        if(ds == null){
+            DynamicDataSource.setDataSource(DataSourceNames.FIRST);
+            logger.debug("set datasource is " + DataSourceNames.FIRST);
+        }else {
+            DynamicDataSource.setDataSource(ds.name());
+            logger.debug("set datasource is " + ds.name());
+        }
+
+        try {
+            return point.proceed();
+        } finally {
+            DynamicDataSource.clearDataSource();
+            logger.debug("clean datasource");
+        }
+    }
+
+    @Override
+    public int getOrder() {
+        return 1;
+    }
+}

+ 0 - 23
src/main/java/io/renren/dynamicdatasource/DataSourceContext.java

@@ -1,23 +0,0 @@
-package io.renren.dynamicdatasource;
-
-/**
- * 增加多数据源,在此配置
- *
- * @author chenshun
- * @email sunlightcs@gmail.com
- * @date 2017/8/18 23:46
- */
-public enum DataSourceContext {
-    FIRST("first"),
-    SECOND("second");
-
-    private String name;
-
-    DataSourceContext(String name) {
-        this.name = name;
-    }
-
-    public String getName() {
-        return name;
-    }
-}

+ 0 - 46
src/main/java/io/renren/modules/api/controller/ApiTestController.java

@@ -1,46 +0,0 @@
-package io.renren.modules.api.controller;
-
-
-import io.renren.common.utils.R;
-import io.renren.modules.api.annotation.AuthIgnore;
-import io.renren.modules.api.annotation.LoginUser;
-import io.renren.modules.api.entity.TokenEntity;
-import io.renren.modules.api.entity.UserEntity;
-import org.springframework.web.bind.annotation.*;
-
-/**
- * API测试接口
- *
- * @author chenshun
- * @email sunlightcs@gmail.com
- * @date 2017-03-23 15:47
- */
-@RestController
-@RequestMapping("/api")
-public class ApiTestController {
-
-    /**
-     * 获取用户信息
-     */
-    @GetMapping("userInfo")
-    public R userInfo(@LoginUser UserEntity user){
-        return R.ok().put("user", user);
-    }
-
-    /**
-     * 忽略Token验证测试
-     */
-    @AuthIgnore
-    @GetMapping("notToken")
-    public R notToken(){
-        return R.ok().put("msg", "无需token也能访问。。。");
-    }
-
-    /**
-     * 接收JSON数据
-     */
-    @PostMapping("jsonData")
-    public R jsonData(@LoginUser UserEntity user, @RequestBody TokenEntity token){
-        return R.ok().put("user", user).put("token", token);
-    }
-}

+ 0 - 21
src/main/java/io/renren/modules/api/dao/TokenDao.java

@@ -1,21 +0,0 @@
-package io.renren.modules.api.dao;
-
-import io.renren.modules.api.entity.TokenEntity;
-import io.renren.modules.sys.dao.BaseDao;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * 用户Token
- * 
- * @author chenshun
- * @email sunlightcs@gmail.com
- * @date 2017-03-23 15:22:07
- */
-@Mapper
-public interface TokenDao extends BaseDao<TokenEntity> {
-    
-    TokenEntity queryByUserId(Long userId);
-
-    TokenEntity queryByToken(String token);
-	
-}

+ 0 - 75
src/main/java/io/renren/modules/api/entity/TokenEntity.java

@@ -1,75 +0,0 @@
-package io.renren.modules.api.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-
-
-
-/**
- * 用户Token
- * 
- * @author chenshun
- * @email sunlightcs@gmail.com
- * @date 2017-03-23 15:22:07
- */
-public class TokenEntity implements Serializable {
-	private static final long serialVersionUID = 1L;
-	
-	//用户ID
-	private Long userId;
-	//token
-	private String token;
-	//过期时间
-	private Date expireTime;
-	//更新时间
-	private Date updateTime;
-
-	/**
-	 * 设置:用户ID
-	 */
-	public void setUserId(Long userId) {
-		this.userId = userId;
-	}
-	/**
-	 * 获取:用户ID
-	 */
-	public Long getUserId() {
-		return userId;
-	}
-	/**
-	 * 设置:token
-	 */
-	public void setToken(String token) {
-		this.token = token;
-	}
-	/**
-	 * 获取:token
-	 */
-	public String getToken() {
-		return token;
-	}
-	/**
-	 * 设置:过期时间
-	 */
-	public void setExpireTime(Date expireTime) {
-		this.expireTime = expireTime;
-	}
-	/**
-	 * 获取:过期时间
-	 */
-	public Date getExpireTime() {
-		return expireTime;
-	}
-	/**
-	 * 设置:更新时间
-	 */
-	public void setUpdateTime(Date updateTime) {
-		this.updateTime = updateTime;
-	}
-	/**
-	 * 获取:更新时间
-	 */
-	public Date getUpdateTime() {
-		return updateTime;
-	}
-}

+ 0 - 32
src/main/java/io/renren/modules/api/service/TokenService.java

@@ -1,32 +0,0 @@
-package io.renren.modules.api.service;
-
-
-import io.renren.modules.api.entity.TokenEntity;
-
-import java.util.Map;
-
-/**
- * 用户Token
- * 
- * @author chenshun
- * @email sunlightcs@gmail.com
- * @date 2017-03-23 15:22:07
- */
-public interface TokenService {
-
-	TokenEntity queryByUserId(Long userId);
-
-	TokenEntity queryByToken(String token);
-	
-	void save(TokenEntity token);
-	
-	void update(TokenEntity token);
-
-	/**
-	 * 生成token
-	 * @param userId  用户ID
-	 * @return        返回token相关信息
-	 */
-	Map<String, Object> createToken(long userId);
-
-}

+ 0 - 80
src/main/java/io/renren/modules/api/service/impl/TokenServiceImpl.java

@@ -1,80 +0,0 @@
-package io.renren.modules.api.service.impl;
-
-import org.springframework.stereotype.Service;
-
-
-import io.renren.modules.api.dao.TokenDao;
-import io.renren.modules.api.entity.TokenEntity;
-import io.renren.modules.api.service.TokenService;
-import org.springframework.beans.factory.annotation.Autowired;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
-
-@Service("tokenService")
-public class TokenServiceImpl implements TokenService {
-	@Autowired
-	private TokenDao tokenDao;
-	//12小时后过期
-	private final static int EXPIRE = 3600 * 12;
-
-	@Override
-	public TokenEntity queryByUserId(Long userId) {
-		return tokenDao.queryByUserId(userId);
-	}
-
-	@Override
-	public TokenEntity queryByToken(String token) {
-		return tokenDao.queryByToken(token);
-	}
-
-	@Override
-	public void save(TokenEntity token){
-		tokenDao.save(token);
-	}
-	
-	@Override
-	public void update(TokenEntity token){
-		tokenDao.update(token);
-	}
-
-	@Override
-	public Map<String, Object> createToken(long userId) {
-		//生成一个token
-		String token = UUID.randomUUID().toString();
-		//当前时间
-		Date now = new Date();
-
-		//过期时间
-		Date expireTime = new Date(now.getTime() + EXPIRE * 1000);
-
-		//判断是否生成过token
-		TokenEntity tokenEntity = queryByUserId(userId);
-		if(tokenEntity == null){
-			tokenEntity = new TokenEntity();
-			tokenEntity.setUserId(userId);
-			tokenEntity.setToken(token);
-			tokenEntity.setUpdateTime(now);
-			tokenEntity.setExpireTime(expireTime);
-
-			//保存token
-			save(tokenEntity);
-		}else{
-			tokenEntity.setToken(token);
-			tokenEntity.setUpdateTime(now);
-			tokenEntity.setExpireTime(expireTime);
-
-			//更新token
-			update(tokenEntity);
-		}
-
-		Map<String, Object> map = new HashMap<>();
-		map.put("token", token);
-		map.put("expire", EXPIRE);
-
-		return map;
-	}
-}

+ 4 - 5
src/main/java/io/renren/modules/api/annotation/AuthIgnore.java

@@ -1,16 +1,15 @@
-package io.renren.modules.api.annotation;
+package io.renren.modules.app.annotation;
 
 import java.lang.annotation.*;
 
 /**
- * api接口,忽略Token验证
+ * app登录效验
  * @author chenshun
  * @email sunlightcs@gmail.com
- * @date 2017-03-23 15:44
+ * @date 2017/9/23 14:30
  */
 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
-public @interface AuthIgnore {
-
+public @interface Login {
 }

+ 1 - 1
src/main/java/io/renren/modules/api/annotation/LoginUser.java

@@ -1,4 +1,4 @@
-package io.renren.modules.api.annotation;
+package io.renren.modules.app.annotation;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;

+ 4 - 4
src/main/java/io/renren/modules/api/config/WebMvcConfig.java

@@ -1,7 +1,7 @@
-package io.renren.modules.api.config;
+package io.renren.modules.app.config;
 
-import io.renren.modules.api.interceptor.AuthorizationInterceptor;
-import io.renren.modules.api.resolver.LoginUserHandlerMethodArgumentResolver;
+import io.renren.modules.app.interceptor.AuthorizationInterceptor;
+import io.renren.modules.app.resolver.LoginUserHandlerMethodArgumentResolver;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@@ -26,7 +26,7 @@ public class WebMvcConfig extends WebMvcConfigurerAdapter {
 
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
-        registry.addInterceptor(authorizationInterceptor).addPathPatterns("/api/**");
+        registry.addInterceptor(authorizationInterceptor).addPathPatterns("/app/**");
     }
 
     @Override

+ 12 - 9
src/main/java/io/renren/modules/api/controller/ApiLoginController.java

@@ -1,37 +1,36 @@
-package io.renren.modules.api.controller;
+package io.renren.modules.app.controller;
 
 
+import io.renren.common.utils.JwtUtils;
 import io.renren.common.utils.R;
 import io.renren.common.validator.Assert;
-import io.renren.modules.api.annotation.AuthIgnore;
-import io.renren.modules.api.service.TokenService;
-import io.renren.modules.api.service.UserService;
+import io.renren.modules.app.service.UserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.HashMap;
 import java.util.Map;
 
 /**
- * API登录授权
+ * APP登录授权
  *
  * @author chenshun
  * @email sunlightcs@gmail.com
  * @date 2017-03-23 15:31
  */
 @RestController
-@RequestMapping("/api")
+@RequestMapping("/app")
 public class ApiLoginController {
     @Autowired
     private UserService userService;
     @Autowired
-    private TokenService tokenService;
+    private JwtUtils jwtUtils;
 
     /**
      * 登录
      */
-    @AuthIgnore
     @PostMapping("login")
     public R login(String mobile, String password){
         Assert.isBlank(mobile, "手机号不能为空");
@@ -41,7 +40,11 @@ public class ApiLoginController {
         long userId = userService.login(mobile, password);
 
         //生成token
-        Map<String, Object> map = tokenService.createToken(userId);
+        String token = jwtUtils.generateToken(userId);
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("token", token);
+        map.put("expire", jwtUtils.getExpire());
 
         return R.ok(map);
     }

+ 3 - 5
src/main/java/io/renren/modules/api/controller/ApiRegisterController.java

@@ -1,10 +1,9 @@
-package io.renren.modules.api.controller;
+package io.renren.modules.app.controller;
 
 
 import io.renren.common.utils.R;
 import io.renren.common.validator.Assert;
-import io.renren.modules.api.annotation.AuthIgnore;
-import io.renren.modules.api.service.UserService;
+import io.renren.modules.app.service.UserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -17,7 +16,7 @@ import org.springframework.web.bind.annotation.RestController;
  * @date 2017-03-26 17:27
  */
 @RestController
-@RequestMapping("/api")
+@RequestMapping("/app")
 public class ApiRegisterController {
     @Autowired
     private UserService userService;
@@ -25,7 +24,6 @@ public class ApiRegisterController {
     /**
      * 注册
      */
-    @AuthIgnore
     @PostMapping("register")
     public R register(String mobile, String password){
         Assert.isBlank(mobile, "手机号不能为空");

+ 50 - 0
src/main/java/io/renren/modules/app/controller/ApiTestController.java

@@ -0,0 +1,50 @@
+package io.renren.modules.app.controller;
+
+
+import io.renren.common.utils.R;
+import io.renren.modules.app.annotation.Login;
+import io.renren.modules.app.annotation.LoginUser;
+import io.renren.modules.app.entity.UserEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * APP测试接口
+ *
+ * @author chenshun
+ * @email sunlightcs@gmail.com
+ * @date 2017-03-23 15:47
+ */
+@RestController
+@RequestMapping("/app")
+public class ApiTestController {
+
+    /**
+     * 获取用户信息
+     */
+    @Login
+    @GetMapping("userInfo")
+    public R userInfo(@LoginUser UserEntity user){
+        return R.ok().put("user", user);
+    }
+
+    /**
+     * 获取用户ID
+     */
+    @Login
+    @GetMapping("userId")
+    public R userInfo(@RequestAttribute("userId") Integer userId){
+        return R.ok().put("userId", userId);
+    }
+
+    /**
+     * 忽略Token验证测试
+     */
+    @GetMapping("notToken")
+    public R notToken(){
+        return R.ok().put("msg", "无需token也能访问。。。");
+    }
+
+}

+ 2 - 2
src/main/java/io/renren/modules/api/dao/UserDao.java

@@ -1,6 +1,6 @@
-package io.renren.modules.api.dao;
+package io.renren.modules.app.dao;
 
-import io.renren.modules.api.entity.UserEntity;
+import io.renren.modules.app.entity.UserEntity;
 import io.renren.modules.sys.dao.BaseDao;
 import org.apache.ibatis.annotations.Mapper;
 

+ 1 - 1
src/main/java/io/renren/modules/api/entity/UserEntity.java

@@ -1,4 +1,4 @@
-package io.renren.modules.api.entity;
+package io.renren.modules.app.entity;
 
 import java.io.Serializable;
 import java.util.Date;

+ 19 - 21
src/main/java/io/renren/modules/api/interceptor/AuthorizationInterceptor.java

@@ -1,12 +1,13 @@
-package io.renren.modules.api.interceptor;
+package io.renren.modules.app.interceptor;
 
 
+import io.jsonwebtoken.Claims;
 import io.renren.common.exception.RRException;
-import io.renren.modules.api.annotation.AuthIgnore;
-import io.renren.modules.api.entity.TokenEntity;
-import io.renren.modules.api.service.TokenService;
+import io.renren.common.utils.JwtUtils;
+import io.renren.modules.app.annotation.Login;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Component;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
@@ -23,44 +24,41 @@ import javax.servlet.http.HttpServletResponse;
 @Component
 public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
     @Autowired
-    private TokenService tokenService;
+    private JwtUtils jwtUtils;
 
-    public static final String LOGIN_USER_KEY = "LOGIN_USER_KEY";
+    public static final String USER_KEY = "userId";
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        AuthIgnore annotation;
+        Login annotation;
         if(handler instanceof HandlerMethod) {
-            annotation = ((HandlerMethod) handler).getMethodAnnotation(AuthIgnore.class);
+            annotation = ((HandlerMethod) handler).getMethodAnnotation(Login.class);
         }else{
             return true;
         }
 
-        //如果有@AuthIgnore注解,则不验证token
-        if(annotation != null){
+        if(annotation == null){
             return true;
         }
 
-        //从header中获取token
-        String token = request.getHeader("token");
-        //如果header中不存在token,则从参数中获取token
+        //获取用户凭证
+        String token = request.getHeader(jwtUtils.getHeader());
         if(StringUtils.isBlank(token)){
-            token = request.getParameter("token");
+            token = request.getParameter(jwtUtils.getHeader());
         }
 
-        //token为空
+        //凭证为空
         if(StringUtils.isBlank(token)){
-            throw new RRException("token不能为空");
+            throw new RRException(jwtUtils.getHeader() + "不能为空", HttpStatus.UNAUTHORIZED.value());
         }
 
-        //查询token信息
-        TokenEntity tokenEntity = tokenService.queryByToken(token);
-        if(tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()){
-            throw new RRException("token失效,请重新登录");
+        Claims claims = jwtUtils.getClaimByToken(token);
+        if(claims == null || jwtUtils.isTokenExpired(claims.getExpiration())){
+            throw new RRException(jwtUtils.getHeader() + "失效,请重新登录", HttpStatus.UNAUTHORIZED.value());
         }
 
         //设置userId到request里,后续根据userId,获取用户信息
-        request.setAttribute(LOGIN_USER_KEY, tokenEntity.getUserId());
+        request.setAttribute(USER_KEY, Long.parseLong(claims.getSubject()));
 
         return true;
     }

+ 6 - 6
src/main/java/io/renren/modules/api/resolver/LoginUserHandlerMethodArgumentResolver.java

@@ -1,9 +1,9 @@
-package io.renren.modules.api.resolver;
+package io.renren.modules.app.resolver;
 
-import io.renren.modules.api.annotation.LoginUser;
-import io.renren.modules.api.entity.UserEntity;
-import io.renren.modules.api.interceptor.AuthorizationInterceptor;
-import io.renren.modules.api.service.UserService;
+import io.renren.modules.app.annotation.LoginUser;
+import io.renren.modules.app.entity.UserEntity;
+import io.renren.modules.app.interceptor.AuthorizationInterceptor;
+import io.renren.modules.app.service.UserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.MethodParameter;
 import org.springframework.stereotype.Component;
@@ -33,7 +33,7 @@ public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgu
     public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
                                   NativeWebRequest request, WebDataBinderFactory factory) throws Exception {
         //获取用户ID
-        Object object = request.getAttribute(AuthorizationInterceptor.LOGIN_USER_KEY, RequestAttributes.SCOPE_REQUEST);
+        Object object = request.getAttribute(AuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST);
         if(object == null){
             return null;
         }

+ 2 - 2
src/main/java/io/renren/modules/api/service/UserService.java

@@ -1,7 +1,7 @@
-package io.renren.modules.api.service;
+package io.renren.modules.app.service;
 
 
-import io.renren.modules.api.entity.UserEntity;
+import io.renren.modules.app.entity.UserEntity;
 
 import java.util.List;
 import java.util.Map;

+ 4 - 4
src/main/java/io/renren/modules/api/service/impl/UserServiceImpl.java

@@ -1,11 +1,11 @@
-package io.renren.modules.api.service.impl;
+package io.renren.modules.app.service.impl;
 
 
 import io.renren.common.exception.RRException;
 import io.renren.common.validator.Assert;
-import io.renren.modules.api.dao.UserDao;
-import io.renren.modules.api.entity.UserEntity;
-import io.renren.modules.api.service.UserService;
+import io.renren.modules.app.dao.UserDao;
+import io.renren.modules.app.entity.UserEntity;
+import io.renren.modules.app.service.UserService;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;

+ 1 - 1
src/main/java/io/renren/modules/job/utils/ScheduleUtils.java

@@ -59,7 +59,7 @@ public class ScheduleUtils {
 
             //放入参数,运行时的方法可以获取
             jobDetail.getJobDataMap().put(ScheduleJobEntity.JOB_PARAM_KEY, new Gson().toJson(scheduleJob));
-            
+
             scheduler.scheduleJob(jobDetail, trigger);
             
             //暂停任务

+ 5 - 5
src/main/java/io/renren/modules/oss/cloud/AliyunCloudStorageService.java

@@ -12,7 +12,7 @@ import java.io.InputStream;
  * @email sunlightcs@gmail.com
  * @date 2017-03-26 16:22
  */
-public class AliyunCloudStorageService extends CloudStorageService{
+public class AliyunCloudStorageService extends CloudStorageService {
     private OSSClient client;
 
     public AliyunCloudStorageService(CloudStorageConfig config){
@@ -44,12 +44,12 @@ public class AliyunCloudStorageService extends CloudStorageService{
     }
 
     @Override
-    public String upload(byte[] data) {
-        return upload(data, getPath(config.getAliyunPrefix()));
+    public String uploadSuffix(byte[] data, String suffix) {
+        return upload(data, getPath(config.getAliyunPrefix(), suffix));
     }
 
     @Override
-    public String upload(InputStream inputStream) {
-        return upload(inputStream, getPath(config.getAliyunPrefix()));
+    public String uploadSuffix(InputStream inputStream, String suffix) {
+        return upload(inputStream, getPath(config.getAliyunPrefix(), suffix));
     }
 }

+ 9 - 6
src/main/java/io/renren/modules/oss/cloud/CloudStorageService.java

@@ -20,9 +20,10 @@ public abstract class CloudStorageService {
     /**
      * 文件路径
      * @param prefix 前缀
+     * @param suffix 后缀
      * @return 返回上传路径
      */
-    public String getPath(String prefix) {
+    public String getPath(String prefix, String suffix) {
         //生成uuid
         String uuid = UUID.randomUUID().toString().replaceAll("-", "");
         //文件路径
@@ -32,7 +33,7 @@ public abstract class CloudStorageService {
             path = prefix + "/" + path;
         }
 
-        return path;
+        return path + suffix;
     }
 
     /**
@@ -45,10 +46,11 @@ public abstract class CloudStorageService {
 
     /**
      * 文件上传
-     * @param data    文件字节数组
-     * @return        返回http地址
+     * @param data     文件字节数组
+     * @param suffix   后缀
+     * @return         返回http地址
      */
-    public abstract String upload(byte[] data);
+    public abstract String uploadSuffix(byte[] data, String suffix);
 
     /**
      * 文件上传
@@ -61,8 +63,9 @@ public abstract class CloudStorageService {
     /**
      * 文件上传
      * @param inputStream  字节流
+     * @param suffix       后缀
      * @return             返回http地址
      */
-    public abstract String upload(InputStream inputStream);
+    public abstract String uploadSuffix(InputStream inputStream, String suffix);
 
 }

+ 20 - 20
src/main/java/io/renren/modules/oss/cloud/QcloudCloudStorageService.java

@@ -2,15 +2,15 @@ package io.renren.modules.oss.cloud;
 
 
 import com.qcloud.cos.COSClient;
-        import com.qcloud.cos.ClientConfig;
-        import com.qcloud.cos.request.UploadFileRequest;
-        import com.qcloud.cos.sign.Credentials;
-        import io.renren.common.exception.RRException;
-        import net.sf.json.JSONObject;
-        import org.apache.commons.io.IOUtils;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.request.UploadFileRequest;
+import com.qcloud.cos.sign.Credentials;
+import io.renren.common.exception.RRException;
+import net.sf.json.JSONObject;
+import org.apache.commons.io.IOUtils;
 
-        import java.io.IOException;
-        import java.io.InputStream;
+import java.io.IOException;
+import java.io.InputStream;
 
 /**
  * 腾讯云存储
@@ -18,7 +18,7 @@ import com.qcloud.cos.COSClient;
  * @email sunlightcs@gmail.com
  * @date 2017-03-26 20:51
  */
-public class QcloudCloudStorageService extends CloudStorageService{
+public class QcloudCloudStorageService extends CloudStorageService {
     private COSClient client;
 
     public QcloudCloudStorageService(CloudStorageConfig config){
@@ -29,15 +29,15 @@ public class QcloudCloudStorageService extends CloudStorageService{
     }
 
     private void init(){
-        Credentials credentials = new Credentials(config.getQcloudAppId(), config.getQcloudSecretId(),
+    	Credentials credentials = new Credentials(config.getQcloudAppId(), config.getQcloudSecretId(),
                 config.getQcloudSecretKey());
-
-        //初始化客户端配置
+    	
+    	//初始化客户端配置
         ClientConfig clientConfig = new ClientConfig();
         //设置bucket所在的区域,华南:gz 华北:tj 华东:sh
         clientConfig.setRegion(config.getQcloudRegion());
-
-        client = new COSClient(clientConfig, credentials);
+        
+    	client = new COSClient(clientConfig, credentials);
     }
 
     @Override
@@ -46,7 +46,7 @@ public class QcloudCloudStorageService extends CloudStorageService{
         if(!path.startsWith("/")) {
             path = "/" + path;
         }
-
+        
         //上传到腾讯云
         UploadFileRequest request = new UploadFileRequest(config.getQcloudBucketName(), path, data);
         String response = client.uploadFile(request);
@@ -61,7 +61,7 @@ public class QcloudCloudStorageService extends CloudStorageService{
 
     @Override
     public String upload(InputStream inputStream, String path) {
-        try {
+    	try {
             byte[] data = IOUtils.toByteArray(inputStream);
             return this.upload(data, path);
         } catch (IOException e) {
@@ -70,12 +70,12 @@ public class QcloudCloudStorageService extends CloudStorageService{
     }
 
     @Override
-    public String upload(byte[] data) {
-        return upload(data, getPath(config.getQcloudPrefix()));
+    public String uploadSuffix(byte[] data, String suffix) {
+        return upload(data, getPath(config.getQcloudPrefix(), suffix));
     }
 
     @Override
-    public String upload(InputStream inputStream) {
-        return upload(inputStream, getPath(config.getQcloudPrefix()));
+    public String uploadSuffix(InputStream inputStream, String suffix) {
+        return upload(inputStream, getPath(config.getQcloudPrefix(), suffix));
     }
 }

+ 5 - 5
src/main/java/io/renren/modules/oss/cloud/QiniuCloudStorageService.java

@@ -17,7 +17,7 @@ import java.io.InputStream;
  * @email sunlightcs@gmail.com
  * @date 2017-03-25 15:41
  */
-public class QiniuCloudStorageService extends CloudStorageService{
+public class QiniuCloudStorageService extends CloudStorageService {
     private UploadManager uploadManager;
     private String token;
 
@@ -59,12 +59,12 @@ public class QiniuCloudStorageService extends CloudStorageService{
     }
 
     @Override
-    public String upload(byte[] data) {
-        return upload(data, getPath(config.getQiniuPrefix()));
+    public String uploadSuffix(byte[] data, String suffix) {
+        return upload(data, getPath(config.getQiniuPrefix(), suffix));
     }
 
     @Override
-    public String upload(InputStream inputStream) {
-        return upload(inputStream, getPath(config.getQiniuPrefix()));
+    public String uploadSuffix(InputStream inputStream, String suffix) {
+        return upload(inputStream, getPath(config.getQiniuPrefix(), suffix));
     }
 }

+ 7 - 7
src/main/java/io/renren/modules/oss/controller/SysOssController.java

@@ -2,16 +2,16 @@ package io.renren.modules.oss.controller;
 
 import com.google.gson.Gson;
 import io.renren.common.exception.RRException;
-import io.renren.modules.oss.entity.SysOssEntity;
-import io.renren.modules.sys.service.SysConfigService;
-import io.renren.modules.oss.service.SysOssService;
 import io.renren.common.utils.*;
-import io.renren.modules.oss.cloud.CloudStorageConfig;
-import io.renren.modules.oss.cloud.OSSFactory;
 import io.renren.common.validator.ValidatorUtils;
 import io.renren.common.validator.group.AliyunGroup;
 import io.renren.common.validator.group.QcloudGroup;
 import io.renren.common.validator.group.QiniuGroup;
+import io.renren.modules.oss.cloud.CloudStorageConfig;
+import io.renren.modules.oss.cloud.OSSFactory;
+import io.renren.modules.oss.entity.SysOssEntity;
+import io.renren.modules.oss.service.SysOssService;
+import io.renren.modules.sys.service.SysConfigService;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -25,7 +25,6 @@ import java.util.List;
 import java.util.Map;
 
 
-
 /**
  * 文件上传
  * 
@@ -110,7 +109,8 @@ public class SysOssController {
 		}
 
 		//上传文件
-		String url = OSSFactory.build().upload(file.getBytes());
+		String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
+		String url = OSSFactory.build().uploadSuffix(file.getBytes(), suffix);
 
 		//保存文件信息
 		SysOssEntity ossEntity = new SysOssEntity();

+ 40 - 1
src/main/java/io/renren/modules/sys/controller/SysLoginController.java

@@ -1,15 +1,24 @@
 package io.renren.modules.sys.controller;
 
+import com.google.code.kaptcha.Constants;
+import com.google.code.kaptcha.Producer;
 import io.renren.common.utils.R;
+import io.renren.common.utils.ShiroUtils;
 import io.renren.modules.sys.entity.SysUserEntity;
 import io.renren.modules.sys.service.SysUserService;
 import io.renren.modules.sys.service.SysUserTokenService;
+import org.apache.commons.io.IOUtils;
 import org.apache.shiro.crypto.hash.Sha256Hash;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.imageio.ImageIO;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.util.Map;
 
@@ -23,15 +32,44 @@ import java.util.Map;
 @RestController
 public class SysLoginController extends AbstractController {
 	@Autowired
+	private Producer producer;
+	@Autowired
 	private SysUserService sysUserService;
 	@Autowired
 	private SysUserTokenService sysUserTokenService;
 
 	/**
+	 * 验证码
+	 */
+	@RequestMapping("captcha.jpg")
+	public void captcha(HttpServletResponse response)throws ServletException, IOException {
+		response.setHeader("Cache-Control", "no-store, no-cache");
+		response.setContentType("image/jpeg");
+
+		//生成文字验证码
+		String text = producer.createText();
+		//生成图片验证码
+		BufferedImage image = producer.createImage(text);
+		//保存到shiro session
+		ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
+
+		ServletOutputStream out = response.getOutputStream();
+		ImageIO.write(image, "jpg", out);
+		IOUtils.closeQuietly(out);
+	}
+
+	/**
 	 * 登录
 	 */
 	@RequestMapping(value = "/sys/login", method = RequestMethod.POST)
-	public Map<String, Object> login(String username, String password)throws IOException {
+	public Map<String, Object> login(String username, String password, String captcha)throws IOException {
+		//本项目已实现,前后端完全分离,但页面还是跟项目放在一起了,所以还是会依赖session
+		//如果想把页面单独放到nginx里,实现前后端完全分离,则需要把验证码注释掉(因为不再依赖session了)
+		String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
+		if(!captcha.equalsIgnoreCase(kaptcha)){
+			return R.error("验证码不正确");
+		}
+
 		//用户信息
 		SysUserEntity user = sysUserService.queryByUserName(username);
 
@@ -50,6 +88,7 @@ public class SysLoginController extends AbstractController {
 		return r;
 	}
 
+
 	/**
 	 * 退出
 	 */

+ 0 - 4
src/main/java/io/renren/modules/sys/controller/SysMenuController.java

@@ -122,10 +122,6 @@ public class SysMenuController extends AbstractController {
 	@RequestMapping("/delete")
 	@RequiresPermissions("sys:menu:delete")
 	public R delete(long menuId){
-		if(menuId <= 30){
-			return R.error("系统菜单,不能删除");
-		}
-
 		//判断是否有子菜单或按钮
 		List<SysMenuEntity> menuList = sysMenuService.queryListParentId(menuId);
 		if(menuList.size() > 0){

+ 2 - 2
src/main/java/io/renren/modules/sys/service/impl/SysConfigServiceImpl.java

@@ -45,12 +45,12 @@ public class SysConfigServiceImpl implements SysConfigService {
 	@Override
 	@Transactional
 	public void deleteBatch(Long[] ids) {
-		sysConfigDao.deleteBatch(ids);
-
 		for(Long id : ids){
 			SysConfigEntity config = queryObject(id);
 			sysConfigRedis.delete(config.getKey());
 		}
+
+		sysConfigDao.deleteBatch(ids);
 	}
 
 	@Override

+ 1 - 1
src/main/resources/application-dev.yml

@@ -8,7 +8,7 @@ spring:
                 username: renren
                 password: 123456
             second:  #数据源2
-                url: jdbc:mysql://localhost:3306/bdshop?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
+                url: jdbc:mysql://localhost:3306/renren_fast?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
                 username: renren
                 password: 123456
             initial-size: 10

+ 10 - 1
src/main/resources/application.yml

@@ -26,7 +26,7 @@ spring:
     redis:
         open: false  # 是否开启redis缓存  true开启   false关闭
         database: 0
-        host: redis.open.renren.io
+        host: localhost
         port: 16379
         password:       # 密码(默认为空)
         timeout: 6000  # 连接超时时长(毫秒)
@@ -41,3 +41,12 @@ mybatis:
     mapperLocations: classpath:mapper/**/*.xml
     configLocation: classpath:mybatis.xml
 
+renren:
+    # APP模块,是通过jwt认证的,如果要使用APP模块,则需要修改【加密秘钥】
+    jwt:
+        # 加密秘钥
+        secret: f4e2e52034348f86b67cde581c0f9eb5[www.renren.io]
+        # token有效时长,7天,单位秒
+        expire: 604800
+        header: token
+

+ 0 - 41
src/main/resources/mapper/api/TokenDao.xml

@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
-<mapper namespace="io.renren.modules.api.dao.TokenDao">
-
-	<select id="queryByUserId" resultType="io.renren.modules.api.entity.TokenEntity">
-		select * from tb_token where user_id = #{value}
-	</select>
-
-	<select id="queryByToken" resultType="io.renren.modules.api.entity.TokenEntity">
-		select * from tb_token where token = #{value}
-	</select>
-	 
-	<insert id="save">
-		insert into tb_token
-		(
-			`user_id`, 
-			`token`, 
-			`expire_time`, 
-			`update_time`
-		)
-		values
-		(
-			#{userId}, 
-			#{token}, 
-			#{expireTime}, 
-			#{updateTime}
-		)
-	</insert>
-	 
-	<update id="update">
-		update tb_token 
-		<set>
-			<if test="token != null">`token` = #{token}, </if>
-			<if test="expireTime != null">`expire_time` = #{expireTime}, </if>
-			<if test="updateTime != null">`update_time` = #{updateTime}</if>
-		</set>
-		where user_id = #{userId}
-	</update>
-
-</mapper>

+ 4 - 4
src/main/resources/mapper/api/UserDao.xml

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
-<mapper namespace="io.renren.modules.api.dao.UserDao">
+<mapper namespace="io.renren.modules.app.dao.UserDao">
 
-	<insert id="save" parameterType="io.renren.modules.api.entity.UserEntity" useGeneratedKeys="true" keyProperty="userId">
+	<insert id="save" parameterType="io.renren.modules.app.entity.UserEntity" useGeneratedKeys="true" keyProperty="userId">
 		insert into tb_user
 		(
 			`username`,
@@ -20,11 +20,11 @@
 			)
 	</insert>
 
-	<select id="queryObject" resultType="io.renren.modules.api.entity.UserEntity">
+	<select id="queryObject" resultType="io.renren.modules.app.entity.UserEntity">
 		select * from tb_user where user_id = #{value}
 	</select>
 
-	<select id="queryByMobile" resultType="io.renren.modules.api.entity.UserEntity">
+	<select id="queryByMobile" resultType="io.renren.modules.app.entity.UserEntity">
 		select * from tb_user where mobile = #{value}
 	</select>
 

+ 1 - 1
src/main/resources/static/js/modules/sys/menu.js

@@ -26,7 +26,7 @@ var vm = new Vue({
         }
     },
     methods: {
-        getMenu: function(menuId){
+        getMenu: function(){
             //加载菜单树
             $.get(baseURL + "sys/menu/select", function(r){
                 ztree = $.fn.zTree.init($("#menuTree"), setting, r.menuList);

+ 1 - 1
src/main/resources/static/js/modules/sys/role.js

@@ -84,7 +84,7 @@ var vm = new Vue({
             vm.title = "修改";
             vm.getMenuTree(roleId);
 		},
-		del: function (event) {
+		del: function () {
 			var roleIds = getSelectedRows();
 			if(roleIds == null){
 				return ;

+ 191 - 0
src/main/resources/static/libs/store.js

@@ -0,0 +1,191 @@
+"use strict"
+// Module export pattern from
+// https://github.com/umdjs/umd/blob/master/returnExports.js
+;(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define([], factory);
+    } else if (typeof exports === 'object') {
+        // Node. Does not work with strict CommonJS, but
+        // only CommonJS-like environments that support module.exports,
+        // like Node.
+        module.exports = factory();
+    } else {
+        // Browser globals (root is window)
+        root.store = factory();
+    }
+}(this, function () {
+
+    // Store.js
+    var store = {},
+        win = (typeof window != 'undefined' ? window : global),
+        doc = win.document,
+        localStorageName = 'localStorage',
+        scriptTag = 'script',
+        storage
+
+    store.disabled = false
+    store.version = '1.3.20'
+    store.set = function(key, value) {}
+    store.get = function(key, defaultVal) {}
+    store.has = function(key) { return store.get(key) !== undefined }
+    store.remove = function(key) {}
+    store.clear = function() {}
+    store.transact = function(key, defaultVal, transactionFn) {
+        if (transactionFn == null) {
+            transactionFn = defaultVal
+            defaultVal = null
+        }
+        if (defaultVal == null) {
+            defaultVal = {}
+        }
+        var val = store.get(key, defaultVal)
+        transactionFn(val)
+        store.set(key, val)
+    }
+    store.getAll = function() {}
+    store.forEach = function() {}
+
+    store.serialize = function(value) {
+        return JSON.stringify(value)
+    }
+    store.deserialize = function(value) {
+        if (typeof value != 'string') { return undefined }
+        try { return JSON.parse(value) }
+        catch(e) { return value || undefined }
+    }
+
+    // Functions to encapsulate questionable FireFox 3.6.13 behavior
+    // when about.config::dom.storage.enabled === false
+    // See https://github.com/marcuswestin/store.js/issues#issue/13
+    function isLocalStorageNameSupported() {
+        try { return (localStorageName in win && win[localStorageName]) }
+        catch(err) { return false }
+    }
+
+    if (isLocalStorageNameSupported()) {
+        storage = win[localStorageName]
+        store.set = function(key, val) {
+            if (val === undefined) { return store.remove(key) }
+            storage.setItem(key, store.serialize(val))
+            return val
+        }
+        store.get = function(key, defaultVal) {
+            var val = store.deserialize(storage.getItem(key))
+            return (val === undefined ? defaultVal : val)
+        }
+        store.remove = function(key) { storage.removeItem(key) }
+        store.clear = function() { storage.clear() }
+        store.getAll = function() {
+            var ret = {}
+            store.forEach(function(key, val) {
+                ret[key] = val
+            })
+            return ret
+        }
+        store.forEach = function(callback) {
+            for (var i=0; i<storage.length; i++) {
+                var key = storage.key(i)
+                callback(key, store.get(key))
+            }
+        }
+    } else if (doc && doc.documentElement.addBehavior) {
+        var storageOwner,
+            storageContainer
+        // Since #userData storage applies only to specific paths, we need to
+        // somehow link our data to a specific path.  We choose /favicon.ico
+        // as a pretty safe option, since all browsers already make a request to
+        // this URL anyway and being a 404 will not hurt us here.  We wrap an
+        // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
+        // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
+        // since the iframe access rules appear to allow direct access and
+        // manipulation of the document element, even for a 404 page.  This
+        // document can be used instead of the current document (which would
+        // have been limited to the current path) to perform #userData storage.
+        try {
+            storageContainer = new ActiveXObject('htmlfile')
+            storageContainer.open()
+            storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'><iframe src="/favicon.ico"></iframe>')
+            storageContainer.close()
+            storageOwner = storageContainer.w.frames[0].document
+            storage = storageOwner.createElement('div')
+        } catch(e) {
+            // somehow ActiveXObject instantiation failed (perhaps some special
+            // security settings or otherwse), fall back to per-path storage
+            storage = doc.createElement('div')
+            storageOwner = doc.body
+        }
+        var withIEStorage = function(storeFunction) {
+            return function() {
+                var args = Array.prototype.slice.call(arguments, 0)
+                args.unshift(storage)
+                // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
+                // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
+                storageOwner.appendChild(storage)
+                storage.addBehavior('#default#userData')
+                storage.load(localStorageName)
+                var result = storeFunction.apply(store, args)
+                storageOwner.removeChild(storage)
+                return result
+            }
+        }
+
+        // In IE7, keys cannot start with a digit or contain certain chars.
+        // See https://github.com/marcuswestin/store.js/issues/40
+        // See https://github.com/marcuswestin/store.js/issues/83
+        var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
+        var ieKeyFix = function(key) {
+            return key.replace(/^d/, '___$&').replace(forbiddenCharsRegex, '___')
+        }
+        store.set = withIEStorage(function(storage, key, val) {
+            key = ieKeyFix(key)
+            if (val === undefined) { return store.remove(key) }
+            storage.setAttribute(key, store.serialize(val))
+            storage.save(localStorageName)
+            return val
+        })
+        store.get = withIEStorage(function(storage, key, defaultVal) {
+            key = ieKeyFix(key)
+            var val = store.deserialize(storage.getAttribute(key))
+            return (val === undefined ? defaultVal : val)
+        })
+        store.remove = withIEStorage(function(storage, key) {
+            key = ieKeyFix(key)
+            storage.removeAttribute(key)
+            storage.save(localStorageName)
+        })
+        store.clear = withIEStorage(function(storage) {
+            var attributes = storage.XMLDocument.documentElement.attributes
+            storage.load(localStorageName)
+            for (var i=attributes.length-1; i>=0; i--) {
+                storage.removeAttribute(attributes[i].name)
+            }
+            storage.save(localStorageName)
+        })
+        store.getAll = function(storage) {
+            var ret = {}
+            store.forEach(function(key, val) {
+                ret[key] = val
+            })
+            return ret
+        }
+        store.forEach = withIEStorage(function(storage, callback) {
+            var attributes = storage.XMLDocument.documentElement.attributes
+            for (var i=0, attr; attr=attributes[i]; ++i) {
+                callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
+            }
+        })
+    }
+
+    try {
+        var testKey = '__storejs__'
+        store.set(testKey, testKey)
+        if (store.get(testKey) != testKey) { store.disabled = true }
+        store.remove(testKey)
+    } catch(e) {
+        store.disabled = true
+    }
+    store.enabled = !store.disabled
+
+    return store
+}));

+ 50 - 36
src/main/resources/views/login.html

@@ -30,16 +30,24 @@
   <div class="login-box-body">
       <p class="login-box-msg">管理员登录</p>
       <div v-if="error" class="alert alert-danger alert-dismissible">
-        <h4 style="margin-bottom: 0px;"><i class="fa fa-exclamation-triangle"></i> {{errorMsg}}</h4>
+        <h4 style="margin-bottom: 0px;"><i class="fa fa-exclamation-circle"></i> {{errorMsg}}</h4>
       </div>
       <div class="form-group has-feedback">
           <input type="text" class="form-control" v-model="username" placeholder="账号">
           <span class="glyphicon glyphicon-user form-control-feedback"></span>
       </div>
       <div class="form-group has-feedback">
-          <input type="password" class="form-control" v-model="password" @keyup.enter="login" placeholder="密码">
+          <input type="password" class="form-control" v-model="password" placeholder="密码">
           <span class="glyphicon glyphicon-lock form-control-feedback"></span>
       </div>
+      <div class="form-group has-feedback">
+          <input type="text" class="form-control" v-model="captcha" @keyup.enter="login" placeholder="验证码">
+          <span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>
+      </div>
+      <div class="form-group has-feedback">
+          <img alt="如果看不清楚,请单击图片刷新!" class="pointer" :src="src" @click="refreshCode">
+          &nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:;" @click="refreshCode">点击刷新</a>
+      </div>
 
       <div class="row">
         <div class="col-xs-8">
@@ -65,40 +73,46 @@
 <script src="libs/app.js"></script>
 <script src="js/common.js"></script>
 <script type="text/javascript">
-var vm = new Vue({
-	el:'#rrapp',
-	data:{
-		username: '',
-		password: '',
-		error: false,
-		errorMsg: ''
-	},
-	beforeCreate: function(){
-		if(self != top){
-			top.location.href = self.location.href;
-		}
-	},
-	methods: {
-		login: function () {
-            var data = "username="+vm.username+"&password="+vm.password;
-			$.ajax({
-				type: "POST",
-			    url: baseURL + "sys/login",
-			    data: data,
-			    dataType: "json",
-			    success: function(r){
-					if(r.code == 0){//登录成功
-                        localStorage.setItem("token", r.token);
-                        parent.location.href ='index.html';
-					}else{
-						vm.error = true;
-						vm.errorMsg = r.msg;
-					}
-				}
-			});
-		}
-	}
-});
+    var vm = new Vue({
+        el:'#rrapp',
+        data:{
+            username: '',
+            password: '',
+            captcha: '',
+            error: false,
+            errorMsg: '',
+            src: 'captcha.jpg'
+        },
+        beforeCreate: function(){
+            if(self != top){
+                top.location.href = self.location.href;
+            }
+        },
+        methods: {
+            refreshCode: function(){
+                this.src = "captcha.jpg?t=" + $.now();
+            },
+            login: function () {
+                var data = "username="+vm.username+"&password="+vm.password+"&captcha="+vm.captcha;
+                $.ajax({
+                    type: "POST",
+                    url: baseURL + "sys/login",
+                    data: data,
+                    dataType: "json",
+                    success: function(r){
+                        if(r.code == 0){//登录成功
+                            localStorage.setItem("token", r.token);
+                            parent.location.href ='index.html';
+                        }else{
+                            vm.error = true;
+                            vm.errorMsg = r.msg;
+                            vm.refreshCode();
+                        }
+                    }
+                });
+            }
+        }
+    });
 </script>
 </body>
 </html>

+ 13 - 9
src/test/java/io/renren/DynamicDataSourceTest.java

@@ -1,9 +1,8 @@
 package io.renren;
 
-import io.renren.dynamicdatasource.DataSourceContext;
-import io.renren.dynamicdatasource.DynamicDataSource;
-import io.renren.modules.api.entity.UserEntity;
-import io.renren.modules.api.service.UserService;
+
+import io.renren.datasources.DataSourceTestService;
+import io.renren.modules.app.entity.UserEntity;
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -16,16 +15,21 @@ import org.springframework.test.context.junit4.SpringRunner;
 @SpringBootTest
 public class DynamicDataSourceTest {
     @Autowired
-    private UserService userService;
+    private DataSourceTestService dataSourceTestService;
 
     @Test
     public void test(){
-        UserEntity user = userService.queryObject(1L);
+        //数据源1
+        UserEntity user = dataSourceTestService.queryObject(1L);
         System.out.println(ToStringBuilder.reflectionToString(user));
 
-        //切换数据源
-        DynamicDataSource.setDataSource(DataSourceContext.SECOND.getName());
-        UserEntity user2 = userService.queryObject(1L);
+        //数据源2
+        UserEntity user2 = dataSourceTestService.queryObject2(1L);
         System.out.println(ToStringBuilder.reflectionToString(user2));
+
+        //数据源1
+        UserEntity user3 = dataSourceTestService.queryObject(1L);
+        System.out.println(ToStringBuilder.reflectionToString(user3));
     }
+
 }

+ 24 - 0
src/test/java/io/renren/JwtTest.java

@@ -0,0 +1,24 @@
+package io.renren;
+
+import io.renren.common.utils.JwtUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class JwtTest {
+    @Autowired
+    private JwtUtils jwtUtils;
+
+    @Test
+    public void test() {
+        String token = jwtUtils.generateToken(1);
+
+        System.out.println(token);
+    }
+
+}