<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qinfen</groupId>
<artifactId>permission</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>permission Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<springframework.version>4.3.10.RELEASE</springframework.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0-b07</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
</dependency>
<!--springmvc+springweb -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- druid数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.20</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.12</version>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-guava</artifactId>
<version>2.5.3</version>
</dependency>
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.8</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.22</version>
</dependency>
<!-- jsp api -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>jsp-api</artifactId>
<version>6.0.36</version>
</dependency>
<!-- validator -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>
<!-- tools -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<!-- jackson -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!-- email -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.4</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<finalName>permission</finalName>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Archetype Created Web Application</display-name>
<!--spring上下文监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring beans 配置文件所在目录 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- spring mvc 配置 -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Encoding编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<!--强制编码 -->
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern><!--拦截所有,包括*.js -->
</filter-mapping>
<!-- druid -->
<servlet>
<servlet-name>DruidStatServlet</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<init-param>
<param-name>loginUsername</param-name>
<param-value>druid</param-value>
</init-param>
<init-param>
<param-name>loginPassword</param-name>
<param-value>druid</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatServlet</servlet-name>
<url-pattern>/sys/druid/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>DruidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
<init-param>
<param-name>exclusions</param-name>
<param-value>*.js,*.css,*.jpg,*.png,*.ico,*.gif,/sys/druid/*</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DruidWebStatFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--登录过滤器 -->
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.mmall.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/sys/*</url-pattern>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
<!--权限过滤器,要写在登录过滤器之后,并且小于等于登录拦截的值 -->
<filter>
<filter-name>aclControlFilter</filter-name>
<filter-class>com.mmall.filter.AclControlFilter</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>exclusionUrls</param-name><!--白名单 -->
<param-value>/sys/user/noAuth.page,/login.page</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>aclControlFilter</filter-name>
<url-pattern>/sys/*</url-pattern>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>signin.jsp</welcome-file>
</welcome-file-list>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.alibaba.com/schema/stat http://www.alibaba.com/schema/stat.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--读取数据库文件 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:settings.properties</value>
</list>
</property>
</bean>
<!--配置Redis -->
<import resource="redis.xml" />
<!--配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${db.driverClassName}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
<property name="initialSize" value="3" />
<property name="minIdle" value="3" />
<property name="maxActive" value="20" />
<property name="maxWait" value="60000" />
<property name="filters" value="stat,wall" />
</bean>
<!--mybatis会话工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:mapper/*.xml" />
</bean>
<!--mybatis接口配置 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.mmall.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- tx事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- druid,对应数据源配置的filters -->
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
<property name="slowSqlMillis" value="3000" />
<property name="logSlowSql" value="true" />
<property name="mergeSql" value="true" />
</bean>
<bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter">
<property name="dbType" value="mysql" />
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--spring管理注解 -->
<context:annotation-config />
<!-- 启动注解驱动的spring mvc 功能 -->
<mvc:annotation-driven />
<!--Http请求前后监听工具被spring管理 -->
<mvc:interceptors>
<bean class="com.mmall.common.HttpInterceptor" />
</mvc:interceptors>
<!-- 启动包扫描功能 -->
<context:component-scan
base-package="com.mmall.controller" />
<context:component-scan
base-package="com.mmall.service" />
<!--允许访问静态资源 -->
<mvc:resources location="/js/" mapping="/js/**" />
<mvc:resources location="/css/" mapping="/css/**" />
<mvc:resources location="/bootstrap3.3.5/"
mapping="/bootstrap3.3.5/**" />
<mvc:resources location="/assets/" mapping="/assets/**" />
<mvc:resources location="/ztree/" mapping="/ztree/**" />
<!--获取spring上下文工具被spring管理,不使用懒加载,项目启动就运行 -->
<bean class="com.mmall.common.ApplicationContextHelper"
lazy-init="false" />
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"
/> -->
<!--在spring中注册全局异常处理类 -->
<bean class="com.mmall.common.SpringExceptionResolver" />
<!--信息返回形式,如json -->
<bean
class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<!--json处理类,在全局异常处理返回结果的时候用到 -->
<bean id="jsonView"
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="safeRowBoundsEnabled" value="true"/>
<setting name="cacheEnabled" value="false" />
<setting name="useGeneratedKeys" value="true" /><!--自动生成主键id -->
</settings>
<!--<typeAliases>-->
<!---->
<!--</typeAliases>-->
<!--<plugins>-->
<!--<plugin interceptor=""></plugin>-->
<!--</plugins>-->
<!--<typeHandlers>-->
<!---->
<!--</typeHandlers>-->
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!--<appender name="permission" class="ch.qos.logback.core.rolling.RollingFileAppender">-->
<!--<file>${catalina.home}/logs/permission.log</file>-->
<!--<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!--<FileNamePattern>${catalina.home}/logs/permission.%d{yyyy-MM-dd}.log.gz</FileNamePattern>-->
<!--</rollingPolicy>-->
<!--<layout class="ch.qos.logback.classic.PatternLayout">-->
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>-->
<!--</layout>-->
<!--</appender>-->
<!---->
<!--<logger name="xxx" level="INFO">-->
<!--<appender-ref ref="permission"/>-->
<!--</logger>-->
<!-- TRACE < DEBUG < INFO < WARN < ERROR -->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
db.username=root
db.password=123456
package com.mmall.common;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
/**
* json数据返回
* @author Administrator
*
*/
@Getter
@Setter
public class JsonData {
private boolean ret;//结果
private String msg;//消息
private Object data;//数据
public JsonData(boolean ret) {
this.ret = ret;
}
public static JsonData success(Object object, String msg) {
JsonData jsonData = new JsonData(true);
jsonData.data = object;
jsonData.msg = msg;
return jsonData;
}
public static JsonData success(Object object) {
JsonData jsonData = new JsonData(true);
jsonData.data = object;
return jsonData;
}
public static JsonData success() {
return new JsonData(true);
}
public static JsonData fail(String msg) {
JsonData jsonData = new JsonData(false);
jsonData.msg = msg;
return jsonData;
}
public Map<String, Object> toMap() {
HashMap<String, Object> result = new HashMap<String, Object>();
result.put("ret", ret);
result.put("msg", msg);
result.put("data", data);
return result;
}
}
package com.mmall.common;
import com.mmall.exception.ParamException;
import com.mmall.exception.PermissionException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 全局异常处理类
* @author Administrator
*
*/
@Slf4j
public class SpringExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//请求的URL
String url = request.getRequestURL().toString();
//处理完成后返回一个视图
ModelAndView mv;
//全局默认异常
String defaultMsg = "System error";
// 这里我们要求项目中所有请求json数据,都使用.json结尾,接收前台数据先校验是否符合实体类要求
if (url.endsWith(".json")) {
//如果这个异常时我们自定义的权限异常或者参数异常
if (ex instanceof PermissionException || ex instanceof ParamException) {
JsonData result = JsonData.fail(ex.getMessage());
//结合spring-servlet.xml中的配置的bean向前台返回一个json格式的数据
mv = new ModelAndView("jsonView", result.toMap());
} else {
//否则按照全局默认异常处理
log.error("unknown json exception, url:" + url, ex);
JsonData result = JsonData.fail(defaultMsg);
mv = new ModelAndView("jsonView", result.toMap());
}
} else if (url.endsWith(".page")){ // 这里我们要求项目中所有请求page页面,都使用.page结尾
log.error("unknown page exception, url:" + url, ex);
JsonData result = JsonData.fail(defaultMsg);
//这里返回的是一个视图,会去找exception.jsp
mv = new ModelAndView("exception", result.toMap());
} else {
log.error("unknow exception, url:" + url, ex);
JsonData result = JsonData.fail(defaultMsg);
mv = new ModelAndView("jsonView", result.toMap());
}
return mv;
}
}
package com.mmall.exception;
/**
* 自定义异常类
* @author Administrator
*
*/
public class PermissionException extends RuntimeException {
public PermissionException() {
super();
}
public PermissionException(String message) {
super(message);
}
public PermissionException(String message, Throwable cause) {
super(message, cause);
}
public PermissionException(Throwable cause) {
super(cause);
}
protected PermissionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.mmall.exception;
/**
* 参数异常类
* @author Administrator
*
*/
public class ParamException extends RuntimeException {
public ParamException() {
super();
}
public ParamException(String message) {
super(message);
}
public ParamException(String message, Throwable cause) {
super(message, cause);
}
public ParamException(Throwable cause) {
super(cause);
}
protected ParamException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.mmall.util;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mmall.exception.ParamException;
import org.apache.commons.collections.MapUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* 数据校验(实体类,基于注解)
* @author Administrator
*
*/
public class BeanValidator {
//全局校验工厂
private static ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
//校验方法,单个类
public static <T> Map<String, String> validate(T t, Class... groups) {
Validator validator = validatorFactory.getValidator();
//获取校验结果
Set validateResult = validator.validate(t, groups);
if (validateResult.isEmpty()) {
return Collections.emptyMap();//没有错误信息返回空map
} else {
//如果出现了异常,封装异常信息
LinkedHashMap errors = Maps.newLinkedHashMap();
Iterator iterator = validateResult.iterator();
while (iterator.hasNext()) {
ConstraintViolation violation = (ConstraintViolation)iterator.next();
//封装有问题的字段和错误信息
errors.put(violation.getPropertyPath().toString(), violation.getMessage());
}
return errors;
}
}
//校验集合
public static Map<String, String> validateList(Collection<?> collection) {
//判断集合是否为空
Preconditions.checkNotNull(collection);
//迭代器
Iterator iterator = collection.iterator();
Map errors;
do {
if (!iterator.hasNext()) {
return Collections.emptyMap();//没有值,返回空集合
}
Object object = iterator.next();//否则获取当前数据
errors = validate(object, new Class[0]);//调用校验单个方法
} while (errors.isEmpty());
return errors;
}
//校验任何对象
public static Map<String, String> validateObject(Object first, Object... objects) {
if (objects != null && objects.length > 0) {
return validateList(Lists.asList(first, objects));
} else {
return validate(first, new Class[0]);
}
}
/**
* 只判断是否有异常
* @param param
* @throws ParamException,抛出自定义的参数异常com.mmall.exception.ParamException
*/
public static void check(Object param) throws ParamException {
Map<String, String> map = BeanValidator.validateObject(param);
if (MapUtils.isNotEmpty(map)) {
throw new ParamException(map.toString());
}
}
}
package com.mmall.param;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 测试校验类
* @author Administrator
*
*/
@Getter
@Setter
public class TestVo {
@NotBlank
private String msg;//不允许为空值
@NotNull(message = "id不可以为空")
@Max(value = 10, message = "id 不能大于10")
@Min(value = 0, message = "id 至少大于等于0")
private Integer id;
private List<String> str;
}
@RequestMapping("/validate.json")
@ResponseBody
public JsonData validate(TestVo vo) throws ParamException {
log.info("validate");
//使用自定义参数校验类去校验接收的参数是否符合要求
BeanValidator.check(vo);
return JsonData.success("test validate");
}
package com.mmall.util;
import lombok.extern.slf4j.Slf4j;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.ser.impl.SimpleFilterProvider;
import org.codehaus.jackson.type.TypeReference;
/**
* json转换工具,类和json的转换
* @author Administrator
*
*/
@Slf4j
public class JsonMapper {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
// config
objectMapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.setFilters(new SimpleFilterProvider().setFailOnUnknownId(false));
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_EMPTY);
}
/**
* 对象转字符串
* @param src
* @return
*/
public static <T> String obj2String(T src) {
if (src == null) {
return null;
}
try {
//如果是字符串,直接输出,否则转换为字符串
return src instanceof String ? (String) src : objectMapper.writeValueAsString(src);
} catch (Exception e) {
log.warn("parse object to String exception, error:{}", e);
return null;
}
}
/**
* 字符串转换为对象
* @param src
* @param typeReference
* @return
*/
public static <T> T string2Obj(String src, TypeReference<T> typeReference) {
if (src == null || typeReference == null) {
return null;
}
try {
return (T) (typeReference.getType().equals(String.class) ? src : objectMapper.readValue(src, typeReference));
} catch (Exception e) {
log.warn("parse String to Object exception, String:{}, TypeReference<T>:{}, error:{}", src, typeReference.getType(), e);
return null;
}
}
}
package com.mmall.common;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 获取spring上下文工具类
* @author Administrator
*
*/
@Component("applicationContextHelper")//被spring管理
public class ApplicationContextHelper implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
/**
* 从applicationContext中取spring上下文的bean
* @param clazz
* @return
*/
public static <T> T popBean(Class<T> clazz) {
if (applicationContext == null) {
return null;
}
return applicationContext.getBean(clazz);
}
public static <T> T popBean(String name, Class<T> clazz) {
if (applicationContext == null) {
return null;
}
return applicationContext.getBean(name, clazz);
}
}
public JsonData validate(TestVo vo) throws ParamException {
SysAclModuleMapper moduleMapper = ApplicationContextHelper.popBean(SysAclModuleMapper.calss);
SysAclModule module = moduleMapper.selectByPrimaryKey(1);
log.info(JsonMapper.obj2String(module));
}
package com.mmall.common;
import com.mmall.util.JsonMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* Http请求前后监听工具
* @author Administrator
*
*/
@Slf4j
public class HttpInterceptor extends HandlerInterceptorAdapter {
private static final String START_TIME = "requestStartTime";
/**
* 请求处理之前调用执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取当前请求路径
String url = request.getRequestURI().toString();
//获取请求参数
Map parameterMap = request.getParameterMap();
log.info("request start. url:{}, params:{}", url, JsonMapper.obj2String(parameterMap));
//记录请求相应时间,开始
long start = System.currentTimeMillis();
request.setAttribute(START_TIME, start);
return true;
}
/**
* 请求处理完之后执行,只执行正常请求
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// String url = request.getRequestURI().toString();
// long start = (Long) request.getAttribute(START_TIME);
// long end = System.currentTimeMillis();
// log.info("request finished. url:{}, cost:{}", url, end - start);
removeThreadLocalInfo();
}
/**
* 请求处理完之后执行,任何请求,包括出现异常后也执行
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String url = request.getRequestURI().toString();
long start = (Long) request.getAttribute(START_TIME);
long end = System.currentTimeMillis();
log.info("request completed. url:{}, cost:{}毫秒", url, end - start);
removeThreadLocalInfo();
}
/**
* 移除当前线程的信息
*/
public void removeThreadLocalInfo() {
RequestHolder.remove();;
}
}
package com.mmall.common;
import com.mmall.model.SysUser;
import javax.servlet.http.HttpServletRequest;
/**
* 请求holder,解决高并发,登录成功后把当前用户和请求存入,可在任意地方取出使用
* @author Administrator
*
*/
public class RequestHolder {
private static final ThreadLocal<SysUser> userHolder = new ThreadLocal<SysUser>();
private static final ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<HttpServletRequest>();
public static void add(SysUser sysUser) {
userHolder.set(sysUser);
}
public static void add(HttpServletRequest request) {
requestHolder.set(request);
}
public static SysUser getCurrentUser() {
return userHolder.get();
}
public static HttpServletRequest getCurrentRequest() {
return requestHolder.get();
}
public static void remove() {
userHolder.remove();
requestHolder.remove();
}
}
package com.mmall.filter;
import com.mmall.common.RequestHolder;
import com.mmall.model.SysUser;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 登录过滤器
* @author Administrator
*
*/
@Slf4j
public class LoginFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
SysUser sysUser = (SysUser)req.getSession().getAttribute("user");
if (sysUser == null) {
String path = "/signin.jsp";
resp.sendRedirect(path);
return;
}
//如果登陆成功把用户信息放到RequestHolder中,使用的时候再取出来
RequestHolder.add(sysUser);
RequestHolder.add(req);
filterChain.doFilter(servletRequest, servletResponse);
return;
}
public void destroy() {
}
}
package com.mmall.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* IP获取工具类,不同服务器有差别
* @author Administrator
*
*/
@Slf4j
public class IpUtil {
public final static String ERROR_IP = "127.0.0.1";
public final static Pattern pattern = Pattern.
compile("(2[5][0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})");
/**
* 取外网IP
*
* @param request
* @return
*/
public static String getRemoteIp(HttpServletRequest request) {
String ip = request.getHeader("x-real-ip");
if (ip == null) {
ip = request.getRemoteAddr();
}
//过滤反向代理的ip
String[] stemps = ip.split(",");
if (stemps != null && stemps.length >= 1) {
//得到第一个IP,即客户端真实IP
ip = stemps[0];
}
ip = ip.trim();
if (ip.length() > 23) {
ip = ip.substring(0, 23);
}
return ip;
}
/**
* 获取用户的真实ip
*
* @param request
* @return
*/
public static String getUserIP(HttpServletRequest request) {
// 优先取X-Real-IP
String ip = request.getHeader("X-Real-IP");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("x-forwarded-for");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if ("0:0:0:0:0:0:0:1".equals(ip)) {
ip = ERROR_IP;
}
}
if ("unknown".equalsIgnoreCase(ip)) {
ip = ERROR_IP;
return ip;
}
int pos = ip.indexOf(',');
if (pos >= 0) {
ip = ip.substring(0, pos);
}
return ip;
}
public static String getLastIpSegment(HttpServletRequest request) {
String ip = getUserIP(request);
if (ip != null) {
ip = ip.substring(ip.lastIndexOf('.') + 1);
} else {
ip = "0";
}
return ip;
}
public static boolean isValidIP(HttpServletRequest request) {
String ip = getUserIP(request);
return isValidIP(ip);
}
/**
* 判断我们获取的ip是否是一个符合规则ip
*
* @param ip
* @return
*/
public static boolean isValidIP(String ip) {
if (StringUtils.isEmpty(ip)) {
log.debug("ip is null. valid result is false");
return false;
}
Matcher matcher = pattern.matcher(ip);
boolean isValid = matcher.matches();
log.debug("valid ip:" + ip + " result is: " + isValid);
return isValid;
}
public static String getLastServerIpSegment() {
String ip = getServerIP();
if (ip != null) {
ip = ip.substring(ip.lastIndexOf('.') + 1);
} else {
ip = "0";
}
return ip;
}
public static String getServerIP() {
InetAddress inet;
try {
inet = InetAddress.getLocalHost();
String hostAddress = inet.getHostAddress();
return hostAddress;
} catch (UnknownHostException e) {
e.printStackTrace();
}
return "127.0.0.1";
}
}
package com.mmall.util;
import com.mmall.beans.Mail;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
/**
* 发送邮件工具类
* @author Administrator
*
*/
@Slf4j
public class MailUtil {
public static boolean send(Mail mail) {
// TODO
String from = "";
int port = 25;
String host = "";
String pass = "";
String nickname = "";
HtmlEmail email = new HtmlEmail();
try {
email.setHostName(host);
email.setCharset("UTF-8");
for (String str : mail.getReceivers()) {
email.addTo(str);
}
email.setFrom(from, nickname);
email.setSmtpPort(port);
email.setAuthentication(from, pass);
email.setSubject(mail.getSubject());
email.setMsg(mail.getMessage());
email.send();
log.info("{} 发送邮件到 {}", from, StringUtils.join(mail.getReceivers(), ","));
return true;
} catch (EmailException e) {
log.error(from + "发送邮件到" + StringUtils.join(mail.getReceivers(), ",") + "失败", e);
return false;
}
}
}
package com.mmall.beans;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import java.util.Set;
/**
* 需要发送的邮件信息
* @author Administrator
*
*/
@Getter
@Setter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Mail {
/**
* 主题
*/
private String subject;
/**
* 内容
*/
private String message;
/**
* 接收者
*/
private Set<String> receivers;
}
package com.mmall.util;
import lombok.extern.slf4j.Slf4j;
import java.security.MessageDigest;
/**
* MD5密码加密工具
* @author Administrator
*
*/
@Slf4j
public class MD5Util {
public static void main(String[] args) {
System.out.println(MD5Util.encrypt("admin"));
}
public final static String encrypt(String s) {
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
try {
byte[] btInput = s.getBytes();
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
log.error("generate md5 error, {}", s, e);
return null;
}
}
}
package com.mmall.util;
import com.mmall.model.SysUser;
import java.util.Date;
import java.util.Random;
/**
* 密码处理工具类
* @author Administrator
*
*/
public class PasswordUtil {
public final static String[] word = {
"a", "b", "c", "d", "e", "f", "g",
"h", "j", "k", "m", "n",
"p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G",
"H", "J", "K", "M", "N",
"P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z"
};
public final static String[] num = {
"2", "3", "4", "5", "6", "7", "8", "9"
};
/**
* 随机生成密码
* @return
*/
public static String randomPassword() {
StringBuffer stringBuffer = new StringBuffer();
//根据时间戳生成随机规则
Random random = new Random(new Date().getTime());
boolean flag = false;
int length = random.nextInt(3) + 8;//密码长度在8到10字符之间
for (int i = 0; i < length; i++) {
if (flag) {
stringBuffer.append(num[random.nextInt(num.length)]);
} else {
stringBuffer.append(word[random.nextInt(word.length)]);
}
flag = !flag;
}
return stringBuffer.toString();
}
public static void main(String[] args) throws Exception {
System.out.println(randomPassword());
Thread.sleep(100);
System.out.println(randomPassword());
Thread.sleep(100);
System.out.println(randomPassword());
}
}
package com.mmall.filter;
import com.google.common.base.Splitter;
import com.google.common.collect.Sets;
import com.mmall.common.ApplicationContextHelper;
import com.mmall.common.JsonData;
import com.mmall.common.RequestHolder;
import com.mmall.model.SysUser;
import com.mmall.service.SysCoreService;
import com.mmall.util.JsonMapper;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 权限拦截
* @author Administrator
*
*/
@Slf4j
public class AclControlFilter implements Filter {
//排除URL
private static Set<String> exclusionUrlSet = Sets.newConcurrentHashSet();
//无权限访问时需要跳转到的页面URL
private final static String noAuthUrl = "/sys/user/noAuth.page";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//定义白名单
String exclusionUrls = filterConfig.getInitParameter("exclusionUrls");
//如果有多个拦截路径,就把他切割开,组装成list
List<String> exclusionUrlList = Splitter.on(",").trimResults().omitEmptyStrings().splitToList(exclusionUrls);
exclusionUrlSet = Sets.newConcurrentHashSet(exclusionUrlList);
exclusionUrlSet.add(noAuthUrl);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//获取请求路径
String servletPath = request.getServletPath();
//获取请求参数列表
Map requestMap = request.getParameterMap();
//请求在白名单内则不拦截,放行
if (exclusionUrlSet.contains(servletPath)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
//取出当前登录用户
SysUser sysUser = RequestHolder.getCurrentUser();
//如果没有登录就访问权限,执行无权限方法
if (sysUser == null) {
log.info("someone visit {}, but no login, parameter:{}", servletPath, JsonMapper.obj2String(requestMap));
noAuth(request, response);
return;
}
//从工具类中获取权限service,进行逻辑处理
SysCoreService sysCoreService = ApplicationContextHelper.popBean(SysCoreService.class);
//是否有权限访问URL
if (!sysCoreService.hasUrlAcl(servletPath)) {
log.info("{} visit {}, but no login, parameter:{}", JsonMapper.obj2String(sysUser), servletPath, JsonMapper.obj2String(requestMap));
noAuth(request, response);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
return;
}
/**
* 无权限访问方法
* @param request
* @param response
* @throws IOException
*/
private void noAuth(HttpServletRequest request, HttpServletResponse response) throws IOException {
String servletPath = request.getServletPath();
if (servletPath.endsWith(".json")) {
JsonData jsonData = JsonData.fail("没有访问权限,如需要访问,请联系管理员");
response.setHeader("Content-Type", "application/json");
response.getWriter().print(JsonMapper.obj2String(jsonData));
return;
} else {
clientRedirect(noAuthUrl, response);
return;
}
}
/**
* 无权限,跳转无权限页面
* @param url
* @param response
* @throws IOException
*/
private void clientRedirect(String url, HttpServletResponse response) throws IOException{
response.setHeader("Content-Type", "text/html");
response.getWriter().print("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" + "<head>\n" + "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"/>\n"
+ "<title>跳转中...</title>\n" + "</head>\n" + "<body>\n" + "跳转中,请稍候...\n" + "<script type=\"text/javascript\">//<![CDATA[\n"
+ "window.location.href='" + url + "?ret='+encodeURIComponent(window.location.href);\n" + "//]]></script>\n" + "</body>\n" + "</html>\n");
}
@Override
public void destroy() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--读取Redis配置文件内容 -->
<context:property-placeholder location="classpath:redis.properties" />
<!--与spring关联的连接池 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" />
<!--核心配置 -->
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool" scope="singleton" >
<constructor-arg index="0" ref="jedisPoolConfig" />
<constructor-arg index="1">
<list>
<bean class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port}"/>
<constructor-arg name="timeout" value="${redis.timeout}"/>
</bean>
</list>
</constructor-arg>
</bean>
</beans>
redis.host=127.0.0.1
redis.port=6379
redis.timeout=3000
package com.mmall.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import javax.annotation.Resource;
/**
* redis配置客户端
* @author Administrator
*
*/
@Service("redisPool")
@Slf4j
public class RedisPool {
@Resource(name = "shardedJedisPool")
private ShardedJedisPool shardedJedisPool;
/**
* 实例化
* @return
*/
public ShardedJedis instance() {
return shardedJedisPool.getResource();
}
/**
* 安全关闭
* @param shardedJedis
*/
public void safeClose(ShardedJedis shardedJedis) {
try {
if (shardedJedis != null) {
shardedJedis.close();
}
} catch (Exception e) {
log.error("return redis resource exception", e);
}
}
}
package com.mmall.service;
import com.google.common.base.Joiner;
import com.mmall.beans.CacheKeyConstants;
import com.mmall.util.JsonMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import redis.clients.jedis.ShardedJedis;
import javax.annotation.Resource;
/**
* 处理缓存cache
* @author Administrator
*
*/
@Service
@Slf4j
public class SysCacheService {
@Resource(name = "redisPool")
private RedisPool redisPool;
/**
* 保存cache
* @param toSavedValue
* @param timeoutSeconds
* @param prefix
*/
public void saveCache(String toSavedValue, int timeoutSeconds, CacheKeyConstants prefix) {
saveCache(toSavedValue, timeoutSeconds, prefix, null);
}
public void saveCache(String toSavedValue, int timeoutSeconds, CacheKeyConstants prefix, String... keys) {
//需要缓存的值为空,返回
if (toSavedValue == null) {
return;
}
//获取连接
ShardedJedis shardedJedis = null;
try {
//生成缓存的key
String cacheKey = generateCacheKey(prefix, keys);
//创建Redis实例
shardedJedis = redisPool.instance();
//执行缓存操作
shardedJedis.setex(cacheKey, timeoutSeconds, toSavedValue);
} catch (Exception e) {
log.error("save cache exception, prefix:{}, keys:{}", prefix.name(), JsonMapper.obj2String(keys), e);
} finally {
redisPool.safeClose(shardedJedis);
}
}
/**
* 获取cache
* @param prefix
* @param keys
* @return
*/
public String getFromCache(CacheKeyConstants prefix, String... keys) {
ShardedJedis shardedJedis = null;
String cacheKey = generateCacheKey(prefix, keys);
try {
shardedJedis = redisPool.instance();
//根据key获取缓存数据
String value = shardedJedis.get(cacheKey);
return value;
} catch (Exception e) {
log.error("get from cache exception, prefix:{}, keys:{}", prefix.name(), JsonMapper.obj2String(keys), e);
return null;
} finally {
redisPool.safeClose(shardedJedis);
}
}
/**
* 自动生成缓存的key
* @param prefix
* @param keys
* @return
*/
private String generateCacheKey(CacheKeyConstants prefix, String... keys) {
//从枚举中获取前缀
String key = prefix.name();
//进行key的拼接
if (keys != null && keys.length > 0) {
key += "_" + Joiner.on("_").join(keys);
}
return key;
}
}
package com.mmall.beans;
import lombok.Getter;
/**
* cache前缀
* @author Administrator
*
*/
@Getter
public enum CacheKeyConstants {
SYSTEM_ACLS,
USER_ACLS;
}
/**
* 从cache中获取当前用户的权限集合
* @return
*/
public List<SysAcl> getCurrentUserAclListFromCache() {
//获取当前用户的id
int userId = RequestHolder.getCurrentUser().getId();
//先从Redis缓存中获取数据
String cacheValue = sysCacheService.getFromCache(CacheKeyConstants.USER_ACLS, String.valueOf(userId));
//如果缓存中没有数据,就去数据库中查询数据,然后放入Redis缓存中
if (StringUtils.isBlank(cacheValue)) {
List<SysAcl> aclList = getCurrentUserAclList();
if (CollectionUtils.isNotEmpty(aclList)) {
sysCacheService.saveCache(JsonMapper.obj2String(aclList), 600, CacheKeyConstants.USER_ACLS, String.valueOf(userId));
}
return aclList;
}
return JsonMapper.string2Obj(cacheValue, new TypeReference<List<SysAcl>>() {
});
}
package com.mmall.util;
import com.google.common.base.Splitter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 字符串解析工具
* @author Administrator
*
*/
public class StringUtil {
public static List<Integer> splitToListInt(String str) {
List<String> strList = Splitter.on(",").trimResults().omitEmptyStrings().splitToList(str);
return strList.stream().map(strItem -> Integer.parseInt(strItem)).collect(Collectors.toList());
}
}
91.什么是 Servlet?
Servlet 是用来处理客户端请求并产生动态网页内容的 Java 类。Servlet 主要是用来处理或者是存储 HTML 表单提交的数据,产生动态内容,在无状态的 HTTP 协议下管理状态信息。
92.说一下 Servlet 的体系结构。
所有的 Servlet 都必须要实现的核心的接口是 javax.servlet.Servlet。每一个 Servlet 都必须要直接或者是间接实现这个接口, 或者是继承 javax.servlet.GenericServlet 或者javax.servlet.http.HTTPServlet。最后,Servlet 使用多线程可以并行的为多个请求服务。
93.Applet 和 Servlet 有什么区别?
Applet 是运行在客户端主机的浏览器上的客户端 Java 程序。而 Servlet 是运行在 web 服务器上的服务端的组件。applet 可以使用用户界面类,而 Servlet 没有用户界面,相反,Servlet是等待客户端的 HTTP 请求,然后为请求产生响应。
94.GenericServlet 和 HttpServlet 有什么区别?
GenericServlet 是一个通用的协议无关的 Servlet,它实现了 Servlet 和 ServletConfig 接口。继承自 GenericServlet 的 Servlet 应该要覆盖 service()方法。最后,为了开发一个能用在网页上服务于使用 HTTP 协议请求的 Servlet,你的 Servlet 必须要继承自 HttpServlet。这里有 Servlet的例子。
95.解释下 Servlet 的生命周期。
Web容器加载Servlet并将其实例化后,Servlet生命周期开始,容器运行其init()方法进行Servlet的初始化;
请求到达时调用Servlet的service()方法,service()方法会根据需要调用与请求对应的doGet或doPost等方法;
当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的destroy()方法。
init方法和destroy方法只会执行一次,service方法客户端每次请求Servlet都会执行。
Servlet中有时会用到一些需要初始化与销毁的资源,因此可以把初始化资源的代码放入init方法中,销毁资源的代码放入destroy方法中
96.doGet()方法和 doPost()方法有什么区别?
①get请求用来从服务器上获得资源,而post是用来向服务器提交数据;
②get将表单中数据按照name=value的形式,添加到action 所指向的URL 后面,并且两者使用"?"连接,而各个变量之间使用"&"连接;post是将表单中的数据放在HTTP协议的请求头或消息体中,传递到action所指向URL;
③get传输的数据要受到URL长度限制(最大长度是 2048 个字符);而post可以传输大量的数据,上传文件通常要使用post方式;
④使用get时参数会显示在地址栏上,如果这些数据不是敏感数据,那么可以使用get;对于敏感数据还是应用使用post;
97.什么是 Web 应用程序?
Web 应用程序是对 Web 或者是应用服务器的动态扩展。有两种类型的 Web 应用:面向表现的和面向服务的。面向表现的 Web 应用程序会产生包含了很多种标记语言和动态内容的交互的web页面作为对请求的响应。而面向服务的Web应用实现了 Web服务的端点(endpoint)。一般来说,一个 Web 应用可以看成是一组安装在服务器 URL 名称空间的特定子集下面的Servlet 的集合。
98.什么是服务端包含(Server Side Include)?
服务端包含(SSI)是一种简单的解释型服务端脚本语言,大多数时候仅用在 Web 上,用 servlet标签嵌入进来。SSI 最常用的场景把一个或多个文件包含到 Web 服务器的一个 Web 页面中。当浏览器访问 Web 页面的时候,Web 服务器会用对应的 servlet 产生的文本来替换 Web 页面中的 servlet 标签。
99.什么是 Servlet 链(Servlet Chaining)?
Servlet 链是把一个 Servlet 的输出发送给另一个 Servlet 的方法。第二个 Servlet 的输出可以发送给第三个 Servlet,依次类推。链条上最后一个 Servlet 负责把响应发送给客户端。
100.如何知道是哪一个客户端的机器正在请求你的 Servlet?
ServletRequest 类可以找出客户端机器的 IP 地址或者是主机名。getRemoteAddr()方法获取客户端主机的 IP 地址,getRemoteHost()可以获取主机名。
101.HTTP 响应的结构是怎么样的?
状态码(Status Code):描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因。如果 Servlet 没有返回状态码,默认会返回成功的状态码 HttpServletResponse.SC_OK。
HTTP 头部(HTTP Header):它们包含了更多关于响应的信息。比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式。如何在 Serlet中检索 HTTP 的头部看这里。
主体(Body):它包含了响应的内容。它可以包含 HTML 代码,图片,等等。主体是由传输在HTTP 消息中紧跟在头部后面的数据字节组成的。
102.什么是 cookie?session 和 cookie 有什么区别?
Cookie 和 Session都是用来跟踪浏览器用户身份的会话方式
Cookie 一般用来保存用户信息 比如①我们在 Cookie 中保存已经登录过得用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了;②一般的网站都会有保持登录也就是说下次你再访问网站的时候就不需要重新登录了,这是因为用户登录的时候我们可以存放了一个 Token 在 Cookie 中,下次登录的时候只需要根据 Token 值来查找用户即可(为了安全考虑,重新登录一般要将 Token 重写);③登录一次网站后访问网站其他页面不需要重新登录。
Session 的主要作用就是通过服务端记录用户的状态。 典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。
Cookie 数据保存在客户端(浏览器端),如果使用 Cookie 的一些敏感信息不要写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。只能存放字符串
Session 数据保存在服务器端。安全性更高,可以存放对象
浏览器和 Servlet 通信使用的是什么协议?
浏览器和 Servlet 通信使用的是 HTTP 协议。
什么是 HTTP 隧道?
HTTP 隧道是一种利用 HTTP 或者是 HTTPS 把多种网络协议封装起来进行通信的技术。因此, HTTP 协议扮演了一个打通用于通信的网络协议的管道的包装器的角色。把其他协议的请求掩盖成 HTTP 的请求就是 HTTP 隧道。
105.sendRedirect()和 forward()方法有什么区别?
转发是服务器行为,重定向是客户端行为。
转发(Forward) 通过RequestDispatcher对象的forward方法实现的。
request.getRequestDispatcher("login_success.jsp").forward(request, response);
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址。
forward:转发页面和转发到的页面可以共享request里面的数据
forward:一般用于用户登陆的时候,根据角色转发到相应的模块。
重定向(Redirect) 是利用服务器返回的状态码来实现的。客户端浏览器请求服务器的时候,服务器会返回一个状态码。服务器通过 HttpServletResponse 的 setStatus(int status) 方法设置状态码。如果服务器返回301或者302,则浏览器会到新的网址重新请求该资源。
redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL。
redirect:不能共享数据。
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等
106.什么是 URL 编码和 URL 解码?
URL 编码是负责把 URL 里面的空格和其他的特殊字符替换成对应的十六进制表示,反之就是解码。
如何让页面做到自动刷新?
自动刷新不仅可以实现一段时间之后自动跳转到另一个页面,还可以实现一段时间之后自动刷新本页面。
Servlet中通过HttpServletResponse对象设置Header属性实现自动刷新
Response.setHeader("Refresh","5;URL=http://localhost:8080/servlet/example.htm");
其中5为时间,单位为秒。URL指定就是要跳转的页面(如果设置自己的路径,就会实现每过5秒自动刷新本页面一次)
如何解决servlet的线程安全问题?
Servlet不是线程安全的,多线程并发的读写会导致数据不同步的问题。
解决的办法是尽量不要定义name属性,而是要把name变量分别定义在doGet()和doPost()方法内。
多线程的并发的读写Servlet类属性会导致数据不同步。但是如果只是并发地读取属性而不写入,则不存在数据不同步的问题。因此Servlet里的只读属性最好定义为final类型的。