目录
静态导入
对于静态方法,使用的时候可以不写上类名,通过import static的方式引用
import static org.springframework.scheduling.support.CronSequenceGenerator.isValidExpression;
import static org.slf4j.LoggerFactory.getLogger; // 静态导入,注意静态导入不要使用"*"的方式,不方便阅读维护
/**
* @author lixx
* @title 动态获取cron表达式
* @date 2019/4/2
*/
@Component
@Service(interfaceClass = CronService.class)
public class CronServiceImpl implements CronService {
// 这里使用静态方法getLogger的时候就可以不写上类名
private static final Logger LOGGER = getLogger(CronServiceImpl.class);
toString工具类
打印实体类的参数一般情况下我们是重写toString方法,会让实体类多一些代码,如果不想重写toString方法又可以和toString效果一样,可以使用ReflectionToStringBuilder工具类
System.out.println(ReflectionToStringBuilder.toString(对象, ToStringStyle.MULTI_LINE_STYLE));
Spring提供的Controller层分页类
Pageable类和@PageableDefault注解,如果项目使用Spring data jpa对分页的处理会非常方便,如果不使用jpa单纯的使用分页类也是可以的
@GetMapping
@JsonView(User.UserSimpleView.class)
@ApiOperation(value = "用户查询服务")
public List<User> query(UserQueryCondition condition,
@PageableDefault(page = 1, size = 10, sort = "username, desc") Pageable pageable) {
mybatis提供的Service层分页插件
1.在pom.xml文件中添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
2.在application.properties配置文件中添加如下配置
# 配置pageHelper分页插件
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
pagehelper.params=count=countSql
3.使用,sql语句不需要写limit这样的分页条件,mybatis插件自动带上了
/**
* 优选会员消费明细
*
* @author lixx
* @date 2019-06-17 11:27:28
* @param spendDTO 入参dto
* @return json集合
*/
@Override
public RequestResult excellentMemberSpendDetails(CrmCustomExcellentSpendDTO spendDTO) {
CrmCustomExcellentSpendVO vo = new CrmCustomExcellentSpendVO();
/**
* 分页插件PageHelper
* 参数一:查询的指定参数页
* 参数二:每页查询多少条数据
*/
PageHelper.startPage(spendDTO.getPageNum(),spendDTO.getPageSize());
List<CrmCustomExcellentDetailsVO> list = crmCustomMapper.selectExcellentMemberSpendDetails(spendDTO);
PageInfo<CrmCustomExcellentDetailsVO> pageInfo = new PageInfo<>(list);
vo.setList(list);
// 使用PageInfo.total获取分页总条数
vo.setTotal(pageInfo.getTotal());
return RequestResult.success(vo);
}
SpringBoot的Test测试类
- 测试方法
import com.zj.br.statistics.BrServiceApplication;
import com.zj.br.statistics.entity.SysTask;
import com.zj.br.statistics.mapper.SysTaskMapper;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.Before;
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.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* 测试方法
*
* @author lixx
* @version 1.0
* @date 2019-07-22 16:14
*/
@RunWith(SpringRunner.class)
// 测试MVC的方式
//@ContextConfiguration("classpath:application.properties")
// 测试SringBoot的方式
@SpringBootTest(classes = BrServiceApplication.class)
public class CronMapperTest {
@Autowired
private SysTaskMapper sysTaskMapper;
@Before
public void before(){
// 可以在这里加一些前置,每次执行@Test之前都会执行该方法
}
/**
* 测试dao层的mapper
*
* @author lixx
* @date 2019-07-22 16:28:53
*/
@Test
public void testSelectByTaskCode(){
SysTask tasks = sysTaskMapper.selectByTaskCode("010");
System.out.println(ReflectionToStringBuilder.toString(tasks, ToStringStyle.MULTI_LINE_STYLE));
}
}
- 测试接口
接口的controller
@PostMapping("/sysTask")
public List<SysTask> test(String username){
System.err.println(username);
List<SysTask> list = new ArrayList<>(3);
list.add(new SysTask());
list.add(new SysTask());
list.add(new SysTask());
return list;
}
接口测试类
import org.junit.Before;
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.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* 测试接口
*
* @author lixx
* @version 1.0
* @date 2019-07-22 16:52
*/
@RunWith(SpringRunner.class)
// 测试MVC的方式
//@ContextConfiguration("classpath:application.properties")
// 测试SringBoot的方式
@SpringBootTest(classes = BrWebApplication.class)
public class CronControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
/**
* 测试controller层接口
*
* @author lixx
* @date 2019-07-22 16:53:50
*/
@Test
public void testSysTask() throws Exception {
String result = mockMvc
.perform(MockMvcRequestBuilders
// get的请求方式,请求的url接口路径
//.get("/sysTask")
// post的请求方式
.post("/sysTask")
// 接口的入参
.param("username", "lixx")
// 请求头json和utf-8编码
.contentType(MediaType.APPLICATION_JSON_UTF8)
)
// 断言,预期的状态码成功200,非200测试失败
// 如果返回结果是乱码, 可以在这里返回ResultActions对象,使用resultActions.andReturn().getResponse().setCharacterEncoding("UTF-8");处理乱码
.andExpect(MockMvcResultMatchers.status().isOk())
// 断言,预期的状态码是4??的
// .andExpect(MockMvcResultMatchers.status().is4xxClientError());
// 断言,预期的结果是个list,长度是3,非指定长度测试失败
// 这里出现了jsonPath表达式,jsonPath表达式的学习请访问官网https://github.com/json-path/JsonPath
.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(3))
.andReturn().getResponse().getContentAsString();
System.err.println(result);
}
}
@JsonView 注解
在返回相同对象的时候,控制指定的资源路径返回指定的字段,这样说可能不太明白,举例说明就是,有一个User对象中有2个字段username,password, 然后有两个请求地址,请求地址1,/getUserList返回User对象的时候不返回password字段,请求地址2,/getUserInfo返回User对象的时候所有字段都返回
使用步骤:
1、在实体类中使用接口来声明多个视图(一个视图显示所有的字段,一个视图不显示某个字段,所以有多个视图)
2、在实体类中字段的get方法上指定视图
public class User {
/**
* 第一步,在实体类中使用接口来声明多个视图
*/
private interface UserSimpleView {};
private interface UserDetailView extends UserSimpleView {};
private String username;
private String password;
/**
* 第二步,在实体类中字段的get方法上指定视图
*/
@JsonView(UserSimpleView.class)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@JsonView(UserDetailView.class)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
3、在Controller方法上指定视图
@GetMapping
// 第三步、在Controller方法上指定视图
@JsonView(User.UserSimpleView.class)
@ApiOperation(value = "用户查询服务")
public List<User> getUserList() {
return null;
}
@GetMapping("/{id:\\d+}")
// 第三步、在Controller方法上指定视图
@JsonView(User.UserDetailView.class)
public User getInfo(@ApiParam("用户id") @PathVariable String id) {
return null;
}
微信抢红包金额随机分配规则
- 微信的金额什么时候算?
在拆红包的时候实时计算出来的,不是预先分配好的,因为预先分配好的要在数据库中存入数据占用内存,如果红包个数为1000岂不是要预先存好1000条数据?而实时算出金额就没有这样的问题了。如果是活动红包,每次红包个数固定且数量不是太多可以采用预先分配好的。
- 金额使用什么规则计算的?
随机计算,计算规则是 在0.01和剩余金额 / 剩余个数 * 2之间的随机数
例如:发100块钱,总共10个红包,那么平均值是10块钱一个,那么发出来的红包的额度在0.01元~20元之间波动。
当前面3个红包总共被领了40块钱时,剩下60块钱,总共7个红包,那么这7个红包的额度在:1~(6000/7*2)=1714之间(单位分),使用Random.nextInt(1714)得到的就是随机金额。
SpringBoot全局异常拦截工具类
在application.properties配置文件中添加如下配置
#出现错误时, 直接抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
#不要为我们工程中的资源文件建立映射
spring.resources.add-mappings=false
copy到项目中直接使用
import com.o2oleader.ylpay.usercenter.utils.JsonResult;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import java.util.HashMap;
import java.util.Map;
/**
* Controller层全局异常处理
*
* @author lixx
* @version 1.0
* @date 2019-10-18 11:18
*/
@RestControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
/**
* 全局所有异常
*
* @param e 异常信息
* @return
*/
@ExceptionHandler(Exception.class)
JsonResult handleException(Exception e) {
e.printStackTrace();
String message = null != e.getCause() ? e.getCause().getMessage() : e.getMessage();
Map<String, Object> map = new HashMap<>();
map.put("code", 500);
map.put("status", "failed");
map.put("message", message);
return JsonResult.newBuilder().setCode("FAIL").setData(map).builder();
}
/**
* 拦截指定异常, 给出明确的异常信息
* redis连接异常
*
* @param e 异常信息
* @return
*/
@ExceptionHandler(RedisConnectionFailureException.class)
JsonResult redisConnectionFailureException(RedisConnectionFailureException e) {
e.printStackTrace();
return JsonResult.newBuilder().setCode("FAIL").setMessage("redis连接异常").builder();
}
/**
* 拦截指定异常, 给出明确的异常信息
* RequestParam注解参数为空异常
*
* @param e 异常信息
* @return
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
JsonResult missingServletRequestParameterException(MissingServletRequestParameterException e) {
e.printStackTrace();
return JsonResult.newBuilder().setCode("FAIL").setMessage(e.getParameterName() + " 参数不能为空").builder();
}
/**
* 拦截指定异常, 给出明确的异常信息
* NotBlank、NotNull、NotEmpty注解参数为空异常
*
* @param e 异常信息
* @return
*/
@ExceptionHandler(BindException.class)
JsonResult bindException(BindException e) {
e.printStackTrace();
return JsonResult.newBuilder().setCode("FAIL").setMessage(e.getBindingResult().getFieldError().getDefaultMessage()).builder();
}
/**
* 拦截指定异常, 给出明确的异常信息
* CustomRuntimeException自定义异常
*
* @param e 异常信息
* @return
*/
@ExceptionHandler(CustomRuntimeException.class)
JsonResult customRuntimeException(CustomRuntimeException e) {
e.printStackTrace();
return JsonResult.newBuilder().setCode("SUCCESS").setMessage(e.getMessage()).builder();
}
/**
* 拦截指定异常, 给出明确的异常信息
* DuplicateKeyException自定义异常
*
* @param e 异常信息
* @return
*/
@ExceptionHandler(DuplicateKeyException.class)
JsonResult duplicateKeyException(DuplicateKeyException e) {
e.printStackTrace();
String result = null;
try {
String message = e.getCause().getMessage();
int index = message.indexOf("'");
index = message.indexOf("'", index + 1);
result = message.substring(message.indexOf("'") + 1, index);
} catch (Exception ex) {
return JsonResult.newBuilder().setCode("FAIL").setMessage("违反唯一性, 数据重复").builder();
}
return JsonResult.newBuilder().setCode("FAIL").setMessage("违反唯一性, 数据'" + result + "'重复").builder();
}
/**
* 404错误
*
* @return
*/
@ExceptionHandler({NoHandlerFoundException.class})
JsonResult noHandlerFoundException(NoHandlerFoundException e) {
Map<String, Object> map = new HashMap<>();
map.put("code", 404);
map.put("status", "failed");
map.put("message", "请求路径不存在");
map.put("path", e.getRequestURL());
map.put("method", e.getHttpMethod());
return JsonResult.newBuilder().setCode("FAIL").setData(map).builder();
}
}
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.io.Serializable;
/**
* @author keven
* @date 2018-04-30 下午11:33
* @Description
*/
public class JsonResult implements Serializable {
@JsonInclude(JsonInclude.Include.NON_NULL)
private Object data;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Object message;
private String code;
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
private long total;
public JsonResult() {
}
public JsonResult(Object data) {
this.code = "SUCCESS";
this.data = data;
}
public JsonResult(Object result, String message) {
this.data = result;
this.message = message;
this.code = "FAIL";
}
public JsonResult(Object data, Object message, String code, long total) {
this.data = data;
this.message = message;
this.code = code;
this.total = total;
}
public static JsonResult success() {
return new JsonResult(true);
}
public static JsonResult success(Object object) {
return new JsonResult(object);
}
public static JsonResult error() {
return new JsonResult(false);
}
public static JsonResult error(String message) {
return new JsonResult(false, message);
}
/**
* @author lixx
* @date 2019-07-25 16:18:28
*/
public static class Builder {
private Object data;
private Object message;
private String code;
private long total;
public JsonResult.Builder setData(Object data) {
this.data = data;
return this;
}
public JsonResult.Builder setMessage(Object message) {
this.message = message;
return this;
}
public JsonResult.Builder setCode(String code) {
this.code = code;
return this;
}
public JsonResult.Builder setTotal(long total) {
this.total = total;
return this;
}
public JsonResult builder() {
return new JsonResult(this.data, this.message, this.code == null ? "SUCCESS" : this.code, this.total);
}
}
public static JsonResult.Builder newBuilder() {
return new JsonResult.Builder();
}
}
SpringBoot使用切面记录日志
1.引入springboot-aop集成jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.application.properties 文件中启用声明
spring.aop.proxy-target-class=true
spring.aop.auto=true
3.自定义一个拦截controller的注解
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD})//作用在参数和方法上
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Documented//表明这个注解应该被 javadoc工具记录
public @interface SystemControllerLog {
String description() default "";
}
4.自定义一个拦截service的注解
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemServiceLog {
String description() default "";
}
5.定义日志切面类
import com.alibaba.fastjson.JSONObject;
import com.o2oleader.ylpay.usercenter.domain.UcUserLog;
import com.o2oleader.ylpay.usercenter.dto.UcUserInfoDto;
import com.o2oleader.ylpay.usercenter.service.UcUserLogService;
import com.o2oleader.ylpay.usercenter.utils.DeviceUtils;
import com.o2oleader.ylpay.usercenter.utils.IPUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Date;
/**
* 日志切面
*
* @author lixx
* @version 1.0
* @date 2019-10-14 11:30:15
*/
@Aspect
@Component
public class SystemLogAspect {
@Autowired
private UcUserLogService ucUserLogService;
//本地异常日志记录对象
private static final Logger LOGGER = LoggerFactory.getLogger(SystemLogAspect.class);
//Service层切点
@Pointcut("@annotation(com.o2oleader.ylpay.usercenter.annotation.SystemServiceLog)")
public void serviceAspect() {
}
//Controller层切点
@Pointcut("@annotation(com.o2oleader.ylpay.usercenter.annotation.SystemControllerLog)")
public void controllerAspect() {
}
/**
* 前置通知 用于拦截Controller层记录用户的操作
*
* @param joinPoint
* @param controllerAspect() 表示使用此注解的方法才会进行切面
* @return
* @author lixx
* @date 2019-10-14 14:45:11
*/
// @After("controllerAspect()")
@Deprecated
public void doBefore(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpSession session = request.getSession();
// ip地址
String ip = IPUtils.getRealIP(request);
}
/**
* 返回结果切面
*
* @param joinPoint
* @param obj
* @param controllerAspect() 表示使用此注解的方法才会进行切面
* @return
* @author lixx
* @date 2019-10-14 15:02:20
*/
@AfterReturning(pointcut = "controllerAspect()", returning = "obj")
public void doAfterReturning(JoinPoint joinPoint, Object obj) {
JSONObject jsonObject = JSONObject.parseObject(obj.toString());
UcUserInfoDto ucUserInfo = jsonObject.getObject("data", UcUserInfoDto.class);
// 登录成功的记录信息
if (null != ucUserInfo) {
LOGGER.info(" ========================== 记录登录日志...");
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
try {
UcUserLog ucUserLog = new UcUserLog();
ucUserLog.setUsername(ucUserInfo.getUsername());
ucUserLog.setStatus(1);
ucUserLog.setType(1);
ucUserLog.setIpAddress(IPUtils.getRealIP(request));
ucUserLog.setDevice(DeviceUtils.isDevice(request));
ucUserLog.setRequestUrl(request.getHeader("Referer"));
ucUserLog.setRequestMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName()));
ucUserLog.setRequestParams(handlerParameter(joinPoint));
ucUserLog.setRequestDescription(getControllerMethodDescription(joinPoint));
ucUserLog.setResponse(JSONObject.toJSONString(ucUserInfo));
ucUserLog.setGmtCreated(new Date());
ucUserLogService.insert(ucUserLog);
} catch (Exception e) {
LOGGER.error("==========登录日志切面异常: {}===========\n", e.getMessage());
}
} else {
LOGGER.info(" ========================== 登录用户为空...");
}
}
/**
* 异常通知 用于拦截service层记录异常日志
*
* @param joinPoint
* @param e
* @return
* @author lixx
* @date 2019-10-14 14:45:24
*/
@AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
try{
/*========控制台输出=========*/
System.out.println("=====异常通知开始=====");
System.out.println("异常代码:" + e.getClass().getName());
System.out.println("异常信息:" + e.getMessage());
System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
System.out.println("方法描述:" + getServiceMethodDescription(joinPoint));
}catch (Exception ex){
//记录本地异常日志
LOGGER.error("==异常通知异常==");
LOGGER.error("异常信息:{}", ex.getMessage());
}
}
/**
* 获取注解中对方法的描述信息 用于service层注解
*
* @param joinPoint
* @return
* @author lixx
* @date 2019-10-14 14:46:00
*/
public static String getServiceMethodDescription(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String description = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
description = method.getAnnotation(SystemServiceLog.class).description();
break;
}
}
}
return description;
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param joinPoint
* @return
* @author lixx
* @date 2019-10-14 14:46:11
*/
public static String getControllerMethodDescription(JoinPoint joinPoint) throws ClassNotFoundException {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();//目标方法名
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String description = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
description = method.getAnnotation(SystemControllerLog.class).description();
break;
}
}
}
return description;
}
private String handlerParameter(JoinPoint point) {
StringBuilder stringBuilder = new StringBuilder();
MethodSignature methodSignature = (MethodSignature) point.getSignature();
String[] parameterNames = methodSignature.getParameterNames();
// Class[] parameterTypes = methodSignature.getParameterTypes();
Object[] args = point.getArgs();
int i = 0;
for (Object pojo : args) {
if (parameterNames[i].equals("password")) {
stringBuilder.append(parameterNames[i]).append("=").append(DigestUtils.md5DigestAsHex(pojo.toString().getBytes())).append("&");
} else {
stringBuilder.append(parameterNames[i]).append("=").append(pojo).append("&");
}
i++;
}
String s = stringBuilder.toString();
return s.substring(0, s.length() - 1);
}
}
6.开始使用切面
@RestController
@RequestMapping("/user")
public class LoginController {
@Autowired
private LoginService loginService;
/**
* 用户登录
*
* @param username 用户名
* @param password 密码
* @return
*/
@PostMapping("/login")
@SystemControllerLog(description = "用户登录") //这里使用切面注解
public JsonResult loginAction(@RequestParam(value = "username") String username, @RequestParam(value = "password") String password) {
Map<String, String> map = new HashMap<>();
map.put("username", username);
map.put("password", DigestUtils.md5DigestAsHex(password.getBytes()));
UcUserInfoDto ucUserInfoDto = loginService.loginService(map);
return JsonResult.success(ucUserInfoDto);
}
}
@Service
public class LoginService {
@SystemServiceLog(description = "用户登录service") // 这里使用切面注解service
public UcUserInfoDto loginService(Map<String, String> map) {}
}
SpringBoot接口访问权限,AOP实现
1.引入springboot-aop集成jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.application.properties 文件中启用声明
spring.aop.proxy-target-class=true
spring.aop.auto=true
3.自定义一个注解,在接口上使用
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 接口权限注解
*
* @author lixx
* @version 1.0
* @date 2019-11-01 9:29
*/
@Target(ElementType.METHOD) // 只能在方法上使用
@Retention(RetentionPolicy.RUNTIME) // 运行时注解
public @interface Authentication {
long[] role() default {1};
}
4.定义切面类,处理验证权限的逻辑
import com.o2oleader.ylpay.usercenter.utils.JsonResult;
import org.apache.commons.lang3.StringUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* 接口访问权限控制
*
* @author lixx
* @version 1.0
* @date 2019-11-01 9:41
*/
@Aspect
@Component
public class AuthenticationAspect {
private static Logger logger = LoggerFactory.getLogger(AuthenticationAspect.class);
// 注解切点
@Pointcut("@annotation(authentication)")
public void authenticationService(Authentication authentication) {
}
/**
* 处理验证权限的逻辑
* @param value = "authenticationService(authentication) // 使用此切点的注解, 参数需要带上切点参数
* @param joinPoint // aop 反射对象信息
* @param authentication // 自定义注解
* @return 权限通过:放行, 权限未通过,return掉
* @throws Throwable // 环绕通知异常, 表示使用此通知的地方如果发生异常, 这里就会产生异常,使用异常catch块可以处理异常通知
*/
@Around(value = "authenticationService(authentication)", argNames = "joinPoint,authentication")
public Object doAround(ProceedingJoinPoint joinPoint, Authentication authentication) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String rolePoint = request.getHeader("role");//获取请求头中的权限,多个权限用逗号分隔
if (StringUtils.isEmpty(rolePoint)) {
return JsonResult.newBuilder().setCode("FAIL").setMessage("权限验证未通过").builder();
}
String[] roleArray = rolePoint.contains(",") ? rolePoint.split(",") : new String[]{rolePoint};
long[] aims = authentication.role();//获取接口允许的权限
boolean isPass = false;
for (long aim : aims) {
if (Arrays.asList(roleArray).contains(String.valueOf(aim))) {
isPass = true;
break;
}
}
if (isPass) {
return joinPoint.proceed();
} else {
logger.error("[authentication] permission verification failed");
return JsonResult.newBuilder().setCode("FAIL").setMessage("权限验证未通过").builder();
}
}
}
5.使用,接口上使用权限注解
@PostMapping("add")
@Authentication(role = {1,2})// 权限注解的使用,表示请求接口的请求头拥有指定的一个权限就可以访问
public JsonResult insertPermissionCode() { }
对象转Json字符串出现大写变小写问题
解决方法:
// 如果需要json字符串中的name首字母大写, 注解中的大写, 变量中的小写
@JSONField(name = "Name")
private String name;
// 不要写成这样, 这样json字符串中还是小写
@JSONField(name = "Name")
private String Name;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72570.html