权限管理开发-准备
一、核心类生成 - Mybatis generate
功能 : MyBatis官方提供了逆向工程 mybatis-generator,可以针对数据库表自动生成MyBatis执行所需要的代码(如Mapper.java、Mapper.xml、POJO)。mybatis-generator 有三种用法:命令行、eclipse插件、maven插件。
1、添加generate插件到permission工程中
根据插件包路径对配置文件 generator.xml 配置文件进行相应修改:
第一点:
修改为mysql-connector-java.5.1.34.jar所在的路径
第二点:
修改数据库连接配置
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://192.168.254.111:3306/test1?characterEncoding=utf8"
userId="root"
password="root"> <!-- 2 -->
知识兔第三点到第五点:
配置生成的类的包及保存路径
第六点:
配置要生成的表及对应的表的实体类名称。
2、执行生成命令:
J:\permission\permission\generator>java -jar mybatis-generator-core-1.3.2.jar -configfile generator.xml
MyBatis Generator finished successfully.
知识兔执行结果:
二、项目接口定义 -json,page
后台收到前台的请求时,一般有两种请求,一种是数据请求,一种页面请求;返回请求结果时,除了返回请求的结果时,还需要返回相关的状态,用于返回异常以及异常描述。
创建JsonData用于封装数据
package com.webcode.springboot.common;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName JsonData
* @Description TODO
* @Author wushaopei
* @Date 2019/10/11 14:05
* @Version 1.0
*/
@Setter
@Getter
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;
}
}
知识兔ret为true代表请求结果为所需要的数据内容,为false时代表请求被拒绝或权限不足、异常等,配合msg进行描述。
三、接口请求全局异常处理-设计与验证
添加相关依赖:
<!--jsp-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>jsp-api</artifactId>
<version>6.0.36</version>
</dependency>
知识兔创建异常拦截类:
/**
* @ClassName SpringExceptionResolver
* @Description TODO
* @Author wushaopei
* @Date 2019/10/11 14:27
* @Version 1.0
*/
@Slf4j
public class SpringExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception ex) {
String url = request.getRequestURL().toString();
String[] split = url.split("");
String s = split[split.length - 1];
int i = s.indexOf("/");
if(s.indexOf("/")==0){
StringBuffer sb = new StringBuffer(url);
url= sb.substring(0, sb.length() - 1);
System.out.println(url);
}
ModelAndView mv; //声明一个modelandvie,用于返回
String defaultMsg = "System error";
//这里区别对数据请求和页面请求进行的处理
//.json , .page ,请求的区分根据其后缀进行区分
//这里要求项目中所有请求json数据,都使用.json结尾
if (url.endsWith(".json")){
}else{
}
return null;
}
}
知识兔这里使用@Slf4j进行日志的输出。
创建自定义异常类 PermissionException:
package com.webcode.springboot.exception;
/**
* @ClassName PermissionException
* @Description TODO
* @Author wushaopei
* @Date 2019/10/11 14:36
* @Version 1.0
*/
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);
}
public PermissionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
知识兔JsonData.java 补充:
public Map<String, Object> toMap(){
HashMap<String, Object> result = new HashMap<>();
result.put("ret",ret);
result.put("msg",msg);
result.put("data",data);
return result;
}
知识兔创建exception.jsp 异常描述页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Exception</title>
</head>
<body>
</body>
</html>
知识兔完整的异常拦截:
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception ex) {
String url = request.getRequestURL().toString();
ModelAndView mv; //声明一个modelandvie,用于返回
String defaultMsg = "System error";
//这里区别对数据请求和页面请求进行的处理
//.json , .page ,请求的区分根据其后缀进行区分
//这里要求项目中所有请求json数据,都使用.json结尾
if (url.endsWith(".json")){
//当前异常是否匹配自定义异常拦截的实例或子类实例
if(ex instanceof PermissionException){
//只要异常是我们创建出来的时,才处理自己抛出来的异常
JsonData fail = JsonData.fail(ex.getMessage());
mv = new ModelAndView("jsonView",fail.toMap());
}else {
log.error("unknown json exception, url:" + url, ex);
JsonData fail = JsonData.fail(defaultMsg);
mv = new ModelAndView("jsonView",fail.toMap());
}
}else if(url.endsWith(".page")){
// 这里我们要求项目中所有请求page页面,都使用.page结尾
log.error("unknown page exception, url:" + url, ex);
JsonData fail = JsonData.fail(defaultMsg);
mv = new ModelAndView("exception",fail.toMap());
}else {
JsonData fail = JsonData.fail(defaultMsg);
mv = new ModelAndView("jsonView",fail.toMap());
}
return mv;
}
知识兔为什么 ModelAndView的构造器加入 ".toMap()"方法:
由图中ModelAndView的源码可知,其构造器默认对数据使用Map进行封装,所以为了符合ModelAndView的代码规范,这里对JsonData的异常返回结果进行Map的转化。
而第一个参数“jsonView”对应spring-servlet.xml中的配置:
Bean配置:
<bean class="com.webcode.springboot.common.SpringExceptionResolver"/>
知识兔接口测试异常拦截:
使用普通的RuntimeException创建异常对象:
@Controller
@RequestMapping("/test")
@Slf4j
public class TestController {
@RequestMapping("/hello.json")
@ResponseBody
public JsonData hello(){
log.info("hello");
throw new RuntimeException("test exception");
}
}
知识兔
使用自定义的PermissionException创建异常对象:
@RequestMapping("/hello.json")
@ResponseBody
public JsonData hello(){
log.info("hello");
throw new PermissionException("test exception");
// return JsonData.success("hello,permission");
}
知识兔
四、校验工具-BeanValidator开发
1、添加环境依赖:
<!--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>
知识兔2、创建BeanValidator类
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();
}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;
}
}
知识兔创建TestVo类:
@Getter
@Setter
public class TestVo {
@NotBlank
private String msg;
@NotNull
private Integer id;
}
知识兔接口测试:
@ResponseBody
@RequestMapping("/validate.json")
public JsonData validate(TestVo vo){
log.info("validate");
try {
Map<String, String> map = BeanValidator.validateObject(vo);
if (map != null && map.entrySet().size() > 0){
for (Map.Entry<String, String> entry : map.entrySet()) {
log.info("{}->{}",entry.getKey(),entry.getValue());
}
}
}catch (Exception e){
}
return JsonData.success("test validate");
}
知识兔没有传参的情况下,控制台打印异常报警日志:
带参数的请求:
http://localhost/test/validate.json/?id=1&msg=123123
知识兔没有异常报警:
BeanValidator的使用:
说明:BeanValidator是基于注解的,
基于@NotBlank、@NotNull、@NotEmpty等注解
@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;
@NotEmpty
private List<String> str;
}
知识兔对Object参数进行判空,不做结果返回:
public static void check(Object param){
//参数异常处理
Map<String, String> map = BeanValidator.validateObject(param);
if(MapUtils.isNotEmpty(map)){
throw new ParamException(map.toString());
}
}
知识兔MapUtils用于判空,需要引入相关依赖:
<!--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>
知识兔创建参数异常处理类 ParamException.java
public class ParamException extends RuntimeException {
public ParamException() {
public ParamException(String message) {
super(message);
}
public ParamException(String message, Throwable cause) {
super(message, cause);
}
public ParamException(Throwable cause) {
super(cause);
}
public ParamException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
知识兔测试:
@ResponseBody
@RequestMapping("/validate2.json")
public JsonData validate2(TestVo vo){
log.info("validate2");
BeanValidator.check(vo);
return JsonData.success("test validate2");
}
知识兔五、权限管理开发 - 工具相关
校验工具 - validator
Json转换工具 - jackson convert
导入相关依赖:
<!--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>
知识兔创建JsonMapper.java类,该类可以把类转换为一个json对象,也可以把类转换为我们指定的类对象。
package com.webcode.springboot.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;
/**
* @ClassName JsonMapper
* @Description TODO
* @Author wushaopei
* @Date 2019/10/11 22:56
* @Version 1.0
*/
@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);
}
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",e);
return null;
}
}
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());
return null;
}
}
}
知识兔六、获取Spring上下文工具-ApplicationContextHelper开发
创建一个获取上下文的类:
package com.webcode.springboot.common;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @ClassName ApplicationContextHelper
* @Description TODO
* @Author wushaopei
* @Date 2019/10/11 23:19
* @Version 1.0
*/
//该注解将当前类交给Spring进行管理
@Component("applicationContextHelper")
public class ApplicationContextHelper implements ApplicationContextAware{
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
applicationContext = applicationContext;
}
public static <T> T popBean(Class<T> clazz){
//如果applicationContext为空,则返回一个空值
if(applicationContext == null){
return null;
}
//否则,就返回当前applicationContext的Bean映射的类
return applicationContext.getBean(clazz);
}
public static <T> T popBean(String name,Class<T> clazz){
//如果applicationContext为空,则返回一个空值
if(applicationContext == null){
return null;
}
//否则,就返回name和当前applicationContext的Bean映射的类
return applicationContext.getBean(name,clazz);
}
}
知识兔配置对应的Bean:
<bean class="com.webcode.springboot.common.ApplicationContextHelper" lazy-init="false"/>
知识兔测试:通过DB进行验证操作:
@ResponseBody
@RequestMapping("/validate3.json")
public JsonData valitade3(TestVo vo){
log.info("validate");
SysAclModuleMapper moduleMapper = ApplicationContextHelper.popBean(SysAclModuleMapper.class);
SysAclModule module = moduleMapper.selectByPrimaryKey(1);
BeanValidator.check(vo);
return JsonData.success("test validate3");
}
知识兔\
七、 Http请求前后监听工具-HttpInterceptor开发
创建HttpInterceptor.java类,继承HandlerInterceptorAdapter类,对Http请求前后进行监听
@Slf4j
public class HttpInterceptor extends HandlerInterceptorAdapter {
@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));
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
String url = request.getRequestURI().toString();
Map parameterMap = request.getParameterMap();
log.info("request finished. url:{}, params:{}",url,JsonMapper.obj2String(parameterMap));
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String url = request.getRequestURI();
Map parameterMap = request.getParameterMap();
log.info("request completed. url:{}, params:{}",url,JsonMapper.obj2String(parameterMap));
}
}
知识兔配置Bean被Spring进行管理:
<mvc:interceptors>
<bean class="com.webcode.springboot.common.HttpInterceptor"/>
</mvc:interceptors>
知识兔测试:
@ResponseBody
@RequestMapping("/validate3.json")
public JsonData valitade3(TestVo vo){
log.info("validate");
SysAclModuleMapper moduleMapper = ApplicationContextHelper.popBean(SysAclModuleMapper.class);
SysAclModule module = moduleMapper.selectByPrimaryKey(1);
log.info(JsonMapper.obj2String(module));
BeanValidator.check(vo);
return JsonData.success("test validate3");
}
知识兔
任何请求在被进行处理前,会先被preHandle方法进行处理,如果该方法处理通过,就会执行postHandle方法,任何请求执行结束后都会被afterCompletion方法进行处理
记录请求开始到结束所花费的时间
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();
Map parameterMap = request.getParameterMap();
long start = (Long) request.getAttribute(START_TIME);
long end = System.currentTimeMillis();
log.info("request finished. url:{}, cost:{}",url,end - start);
}
@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);
}
知识兔
监听器除了可以进行登录时间的监控外,还可进行用户的cookie、session、登录是否过期等进行监听。