用户模块的开发
目录
- 横向越权与纵向越权
- 横向越权:比方说一个通过订单号查询该订单的接口,访问者通过其他订单号可以查询到不是他自己的一个订单信息
- 纵向越权:通过分析接口一步步获取更高权限的功能
数据库sql不要用select * 要什么查什么列,为了以后表的扩展
解决横向越权的方式(使用guava缓存)
使用guava缓存需要导入架包
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
初始化
package com.mmall.common;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* Created by geely
*/
public class TokenCache {
private static Logger logger = LoggerFactory.getLogger(TokenCache.class);
public static final String TOKEN_PREFIX = "token_";
//LRU算法
private static LoadingCache<String,String> localCache = CacheBuilder.newBuilder().initialCapacity(1000).maximumSize(10000).expireAfterAccess(12, TimeUnit.HOURS)
.build(new CacheLoader<String, String>() {
//默认的数据加载实现,当调用get取值的时候,如果key没有对应的值,就调用这个方法进行加载.
@Override
public String load(String s) throws Exception {
return "null";
}
});
public static void setKey(String key,String value){
localCache.put(key,value);
}
public static String getKey(String key){
String value = null;
try {
value = localCache.get(key);
if("null".equals(value)){
return null;
}
return value;
}catch (Exception e){
logger.error("localCache get error",e);
}
return null;
}
}
initialCapacity(1000):设置初始化容量
maximumSize(10000):当容量超过10000会使用LRU算法来移除最少使用的缓存
expireAfterAccess(12, TimeUnit.HOURS):缓存的有效期是12小时
然后使用UUID生成token字符串存入并返回给前台,前台在修改数据的时候需要把token传入进来,后台进行判断就可以解决横向越权的问题
public ServerResponse<String> checkAnswer(String username,String question,String answer){
int resultCount = userMapper.checkAnswer(username,question,answer);
if(resultCount>0){
//说明问题及问题答案是这个用户的,并且是正确的
String forgetToken = UUID.randomUUID().toString();
TokenCache.setKey(TokenCache.TOKEN_PREFIX+username,forgetToken);
return ServerResponse.createBySuccess(forgetToken);
}
return ServerResponse.createByErrorMessage("问题的答案错误");
}
conut(1)比Count(*)性能略高
org.apache.commons.lang3.StringUtils.isBlank(forgetToken)
密码通过MD5加密
package com.mmall.util;
import org.springframework.util.StringUtils;
import java.security.MessageDigest;
/**
* Created by geely
*/
public class MD5Util {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/**
* 返回大写MD5
*
* @param origin
* @param charsetname
* @return
*/
private static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString.toUpperCase();
}
public static String MD5EncodeUtf8(String origin) {
origin = origin + PropertiesUtil.getProperty("password.salt", "");
return MD5Encode(origin, "utf-8");
}
private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
}
通过MD5EncodeUtf8方法传入需要加密的密码,返回一个加密且不可逆的字符串密码,但是很多网站是可以破解的故我们要加盐:PropertiesUtil.getProperty("password.salt", "");通过配置文件读到的如下
package com.mmall.util;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
/**
* Created by geely
*/
public class PropertiesUtil {
private static Logger logger = LoggerFactory.getLogger(PropertiesUtil.class);
private static Properties props;
static {
String fileName = "mmall.properties";
props = new Properties();
try {
props.load(new InputStreamReader(PropertiesUtil.class.getClassLoader().getResourceAsStream(fileName),"UTF-8"));
} catch (IOException e) {
logger.error("配置文件读取异常",e);
}
}
public static String getProperty(String key){
String value = props.getProperty(key.trim());
if(StringUtils.isBlank(value)){
return null;
}
return value.trim();
}
public static String getProperty(String key,String defaultValue){
String value = props.getProperty(key.trim());
if(StringUtils.isBlank(value)){
value = defaultValue;
}
return value.trim();
}
}
ftp.server.ip=ä½ çFTPæå¡å¨ipå°å
ftp.user=mmallftp
ftp.pass=ftppassword
ftp.server.http.prefix=http://img.happymmall.com/
alipay.callback.url=http://www.happymmall.com/order/alipay_callback.do
password.salt = geelysdafaqj23ou89ZXcj@#$@#$#@KJdjklj;D../dSF.,
这样就可以加强密码被破解的难度
logback日志的操作
导入架包
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
<scope>compile</scope>
</dependency>
配置配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoding>UTF-8</encoding>
<encoder>
<pattern>[%d{HH:mm:ss.SSS}][%p][%c{40}][%t] %m%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
</appender>
<appender name="mmall" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--<File>d:/mmalllog/mmall.log</File>-->
<File>/developer/apache-tomcat-7.0.73/logs/mmall.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/developer/apache-tomcat-7.0.73/logs/mmall.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
<append>true</append>
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{HH:mm:ss.SSS}][%p][%c{40}][%t] %m%n</pattern>
</encoder>
</appender>
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--<File>d:/mmalllog/error.log</File>-->
<File>/developer/apache-tomcat-7.0.73/logs/error.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/devsoft/apache-tomcat-7.0.73/logs/error.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
<!--<fileNamePattern>d:/mmalllog/error.log.%d{yyyy-MM-dd}.gz</fileNamePattern>-->
<append>true</append>
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{HH:mm:ss.SSS}][%p][%c{40}][%t] %m%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<logger name="com.mmall" additivity="false" level="INFO" >
<appender-ref ref="mmall" />
<appender-ref ref="console"/>
</logger>
<!-- geelynote mybatis log 日志 -->
<logger name="com.mmall.dao" level="DEBUG"/>
<!--<logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" level="DEBUG" >-->
<!--<appender-ref ref="console"/>-->
<!--</logger>-->
<!--<logger name="java.sql.Connection" level="DEBUG">-->
<!--<appender-ref ref="console"/>-->
<!--</logger>-->
<!--<logger name="java.sql.Statement" level="DEBUG">-->
<!--<appender-ref ref="console"/>-->
<!--</logger>-->
<!--<logger name="java.sql.PreparedStatement" level="DEBUG">-->
<!--<appender-ref ref="console"/>-->
<!--</logger>-->
<root level="DEBUG">
<appender-ref ref="console"/>
<appender-ref ref="error"/>
</root>
</configuration>