Commit 7bbe84e0 authored by liuyang's avatar liuyang

init project

parents
# Created by https://www.gitignore.io/api/eclipse,intellij,maven
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
.DS_Store
.idea
*.iml
# modules.xml
# .idea/misc.xml
# *.ipr
### Maven ###
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
### redis ###
*.rdb
#*.jpg
#*.png
# Exclude maven wrapper
!/.mvn/wrapper/maven-wrapper.jar
# End of https://www.gitignore.io/api/eclipse,intellij,maven
*.iml
*.class
target/*
This diff is collapsed.
FROM anapsix/alpine-java:8u202b08_server-jre_unlimited
ARG JAR_FILE
ADD ${JAR_FILE} $WORKDIR
RUN mv ${JAR_FILE} app.jar
COPY docker-entrypoint.sh /usr/share/app/docker-entrypoint.sh
RUN chmod +x /usr/share/app/docker-entrypoint.sh
ENV MYSQL_URL tcp://mysql:3306
ENV WAIT_MYSQL_TIMEOUT 30s
ENV JAVA_OPTIONS "-Xms256m -Xmx512m -Dfile.encoding=UTF-8"
ENV OVERRIDE_PROP ""
ENTRYPOINT ["/usr/share/app/docker-entrypoint.sh"]
EXPOSE 80
#!/bin/bash
set -ex
java $JAVA_OPTIONS -jar app.jar $OVERRIDE_PROP
package com.qkdata;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.retry.annotation.EnableRetry;
@SpringBootApplication
@ComponentScan(basePackages = {
"com.qkdata"
})
@ServletComponentScan
@EnableCaching
@EnableRetry
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package com.qkdata.biz.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
public enum AccountStatusEnum {
DISABLE(0, "禁用"), ENABLE(1, "启用"),UNACTIVATE(2,"未激活");
@EnumValue
private Integer value;
private String text;
AccountStatusEnum(Integer value, String text) {
this.value = value;
this.text = text;
}
}
package com.qkdata.biz.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
public enum MenuTypeEnum {
FOLDER(0,"目录"),MENU(1,"菜单"),BUTTON(3,"按扭");
MenuTypeEnum(Integer value,String text){
this.value = value;
this.text = text;
}
@EnumValue
private Integer value;
private String text;
}
package com.qkdata.biz.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
public enum SysConfigStatusEnum {
HIDDEN(0, "隐藏"), SHOW(1, "显示");
@EnumValue
private Integer value;
private String text;
SysConfigStatusEnum(Integer value, String text) {
this.value = value;
this.text = text;
}
}
package com.qkdata.biz.sys.controller;
import com.qkdata.biz.sys.entity.SysConfigPO;
import com.qkdata.biz.sys.service.SysConfigService;
import com.qkdata.biz.sys.vo.QueryConfigModel;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.model.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
@Api(tags = "字典表")
@RestController
@RequestMapping("/api/sys/config")
public class SysConfigController {
@Autowired
private SysConfigService sysConfigService;
@ApiOperation("查询列表")
@PostMapping("/list")
public PageResult<SysConfigPO> list(@RequestBody QueryConfigModel queryConfigModel){
return sysConfigService.queryPageList(queryConfigModel);
}
@ApiOperation("获取单条信息")
@GetMapping("/info/{id}")
public Result<SysConfigPO> info(@PathVariable Long id){
return Result.succeed(sysConfigService.getById(id));
}
@SysLog("保存字典数据")
@ApiOperation("保存")
@PostMapping("/save")
public Result<String> save(@RequestBody SysConfigPO sysConfigPO){
sysConfigService.save(sysConfigPO);
return Result.succeed("ok");
}
@SysLog("修改字典数据")
@ApiOperation("更新")
@PostMapping("/update")
public Result<String> update(@RequestBody SysConfigPO sysConfigPO){
sysConfigService.updateById(sysConfigPO);
return Result.succeed("ok");
}
@SysLog("删除字典数据")
@ApiOperation("删除")
@PostMapping("/delete")
public Result<String> delete(@RequestBody Long[] ids){
sysConfigService.removeByIds(Arrays.asList(ids));
return Result.succeed("ok");
}
}
package com.qkdata.biz.sys.controller;
import com.qkdata.biz.sys.entity.SysLogPO;
import com.qkdata.biz.sys.service.SysLogService;
import com.qkdata.biz.sys.vo.QueryLogModel;
import com.qkdata.common.base.model.PageResult;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "审计日志")
@RestController
@RequestMapping("/api/sys/log")
public class SysLogController {
@Autowired
private SysLogService sysLogService;
@PostMapping("/list")
public PageResult<SysLogPO> list(@RequestBody QueryLogModel queryLogModel){
return sysLogService.queryPageList(queryLogModel);
}
}
package com.qkdata.biz.sys.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.qkdata.biz.sys.service.ShiroService;
import com.qkdata.biz.sys.vo.LoginModel;
import com.qkdata.biz.sys.vo.LoginUserInfo;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.base.model.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Api(tags = "系统登陆")
@RestController
@RequestMapping("/api/sys")
public class SysLoginController {
@Autowired
private ShiroService shiroService;
@ApiOperation("登陆")
@SysLog("登陆")
@PostMapping("/login")
public Result<LoginUserInfo> login(@RequestBody @Valid LoginModel loginModel) throws JsonProcessingException {
return Result.succeed(shiroService.login(loginModel.getUsername(),loginModel.getPassword()));
}
@ApiOperation("登出")
@SysLog("登出")
@GetMapping("/logout")
public Result<String> logout(){
SecurityUtils.getSubject().logout();
return Result.succeed("ok");
}
}
package com.qkdata.biz.sys.controller;
import com.qkdata.biz.sys.entity.SysMenuPO;
import com.qkdata.biz.sys.service.ShiroService;
import com.qkdata.biz.sys.service.SysMenuService;
import com.qkdata.biz.sys.vo.SysNavModel;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.Result;
import com.qkdata.common.util.UserContext;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Set;
@Api(tags = "菜单管理")
@RestController
@RequestMapping("/api/sys/menu")
public class SysMenuController {
@Autowired
private SysMenuService sysMenuService;
@Autowired
private ShiroService shiroService;
@ApiOperation("获取当前用户的菜单")
@GetMapping("/nav")
public Result<SysNavModel> nav(){
List<SysMenuPO> menuList = sysMenuService.getUserMenuList(UserContext.getUserId());
Set<String> permissions = shiroService.getUserPermissions(UserContext.getUserId());
SysNavModel navDTO = new SysNavModel(menuList,permissions);
return Result.succeed(navDTO);
}
@ApiOperation("获取全部菜单列表")
@GetMapping("/list")
@RequiresPermissions("sys:menu:list")
public Result<List<SysMenuPO>> list(){
List<SysMenuPO> menuList = sysMenuService.list();
for (SysMenuPO menuPO : menuList){
SysMenuPO parentMenu = sysMenuService.getById(menuPO.getParentId());
if (parentMenu != null){
menuPO.setParentName(parentMenu.getName());
}
}
return Result.succeed(menuList);
}
@ApiOperation("获取不包含按扭的全部菜单")
@GetMapping("/select")
@RequiresPermissions("sys:menu:select")
public Result<List<SysMenuPO>> select(){
List<SysMenuPO> menuList = sysMenuService.queryNotButtonList();
//添加顶级菜单
SysMenuPO root = new SysMenuPO();
root.setId(0L);
root.setName("一级菜单");
root.setParentId(-1L);
root.setOpen(true);
menuList.add(root);
return Result.succeed(menuList);
}
@ApiOperation("获取单个菜单详细信息")
@GetMapping("/info/{id}")
@RequiresPermissions("sys:menu:info")
public Result<SysMenuPO> info(@PathVariable Long id){
SysMenuPO sysMenuPO = sysMenuService.getById(id);
return Result.succeed(sysMenuPO);
}
@ApiOperation("保存菜单信息")
@SysLog("保存菜单信息")
@PostMapping("/save")
@RequiresPermissions("sys:menu:save")
public Result<String> save(@RequestBody SysMenuPO sysMenuPO){
sysMenuService.save(sysMenuPO);
return Result.succeed("ok");
}
@ApiOperation("修改菜单信息")
@SysLog("修改菜单信息")
@PostMapping("/update")
@RequiresPermissions("sys:menu:update")
public Result<String> update(@RequestBody SysMenuPO sysMenuPO){
sysMenuService.updateById(sysMenuPO);
return Result.succeed("ok");
}
@ApiOperation("删除菜单")
@SysLog("删除菜单")
@GetMapping("/delete/{id}")
@RequiresPermissions("sys:menu:delete")
public Result<String> delete(@PathVariable Long id){
if (id <= 31){
throw new BusinessException("系统菜单不能删除");
}
//判断是否有子菜单或按钮
List<SysMenuPO> menuList = sysMenuService.queryListParentId(id);
if(menuList.size() > 0){
throw new BusinessException("请先删除子菜单或按钮");
}
sysMenuService.removeById(id);
return Result.succeed("ok");
}
}
package com.qkdata.biz.sys.controller;
import com.qkdata.biz.sys.entity.SysRolePO;
import com.qkdata.biz.sys.service.SysRoleService;
import com.qkdata.biz.sys.vo.QueryRoleModel;
import com.qkdata.biz.sys.vo.SysRoleModel;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.model.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@Api(tags = "角色管理")
@RestController
@RequestMapping("/api/sys/role")
public class SysRoleController {
@Autowired
private SysRoleService sysRoleService;
/**
* 角色列表
*/
@ApiOperation("获取全部角色列表")
@GetMapping("/select")
@RequiresPermissions("sys:role:select")
public List<SysRolePO> select(){
return sysRoleService.list();
}
@ApiOperation("查询角色列表")
@PostMapping("/list")
@RequiresPermissions("sys:role:list")
public PageResult<SysRolePO> list(@RequestBody QueryRoleModel queryRoleModel){
return sysRoleService.queryPageList(queryRoleModel);
}
@ApiOperation("获取某一角色信息")
@GetMapping("/info/{id}")
@RequiresPermissions("sys:role:info")
public Result<SysRoleModel> info(@PathVariable Long id){
return Result.succeed(sysRoleService.getRoleInfo(id));
}
@ApiOperation("保存某一角色信息")
@SysLog("保存某一角色信息")
@PostMapping("/save")
@RequiresPermissions("sys:role:save")
public Result<String> save(@RequestBody @Validated SysRoleModel sysRoleModel){
sysRoleService.saveRole(sysRoleModel);
return Result.succeed("ok");
}
@ApiOperation("修改某一角色信息")
@SysLog("修改某一角色信息")
@PostMapping("/update")
@RequiresPermissions("sys:role:update")
public Result<String> update(@RequestBody @Validated SysRoleModel sysRoleModel){
sysRoleService.updateRole(sysRoleModel);
return Result.succeed("ok");
}
@ApiOperation("删除角色信息")
@SysLog("删除角色信息")
@PostMapping("/delete")
@RequiresPermissions("sys:role:delete")
public Result<String> delete(@RequestBody Long[] ids){
sysRoleService.removeByIds(Arrays.asList(ids));
return Result.succeed("ok");
}
}
package com.qkdata.biz.sys.controller;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.service.SysUserService;
import com.qkdata.biz.sys.vo.PasswordModel;
import com.qkdata.biz.sys.vo.QueryUserModel;
import com.qkdata.biz.sys.vo.SysUserModel;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.model.Result;
import com.qkdata.common.constants.AddGroup;
import com.qkdata.common.constants.UpdateGroup;
import com.qkdata.common.util.UserContext;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.Arrays;
@Api(tags = "用户管理")
@RestController
@RequestMapping("/api/sys/user")
public class SysUserController {
@Autowired
private SysUserService sysUserService;
@ApiOperation("查询用户列表")
@PostMapping("/list")
@RequiresPermissions("sys:user:list")
public PageResult<SysUserModel> list(@RequestBody QueryUserModel queryUserModel){
return sysUserService.queryPageList(queryUserModel);
}
@ApiOperation("获取当前用户信息")
@GetMapping("/info")
public Result<SysUserModel> info(){
SysUserPO userPO = UserContext.getUser();
SysUserModel dto = new SysUserModel();
BeanUtils.copyProperties(userPO,dto);
dto.setRoleIdList(sysUserService.queryRoleIdList(userPO.getId()));
return Result.succeed(dto);
}
@ApiOperation("获取某个用户信息")
@GetMapping("/info/{id}")
@RequiresPermissions("sys:user:info")
public Result<SysUserModel> infoById(@PathVariable Long id){
SysUserPO sysUserPO = sysUserService.getById(id);
SysUserModel dto = new SysUserModel();
BeanUtils.copyProperties(sysUserPO,dto);
dto.setRoleIdList(sysUserService.queryRoleIdList(id));
return Result.succeed(dto);
}
@ApiOperation("保存用户信息")
@SysLog("保存用户信息")
@PostMapping("/save")
@RequiresPermissions("sys:user:save")
public Result<String> save(@RequestBody @Validated(AddGroup.class) SysUserModel sysUserModel){
sysUserService.saveUser(sysUserModel);
return Result.succeed("ok");
}
@ApiOperation("修改用户信息")
@SysLog("修改用户信息")
@PostMapping("/update")
@RequiresPermissions("sys:user:update")
public Result<String> update(@RequestBody @Validated(UpdateGroup.class) SysUserModel sysUserModel){
sysUserService.updateUser(sysUserModel);
return Result.succeed("ok");
}
/**
* 修改登录用户密码
*/
@SysLog("修改当前用户密码")
@ApiOperation("修改当前用户密码")
@PostMapping("/password")
public Result<String> password(@RequestBody @Valid PasswordModel form){
//更新密码
sysUserService.updatePassword(UserContext.getUser(), form.getPassword(), form.getNewPassword());
return Result.succeed("ok");
}
/**
* 删除用户
*/
@ApiOperation("删除用户")
@SysLog("删除用户")
@PostMapping("/delete")
@RequiresPermissions("sys:user:delete")
public Result<String> delete(@RequestBody Long[] userIds){
if(ArrayUtils.contains(userIds, 1L)){
throw new BusinessException("系统管理员不能删除");
}
if(ArrayUtils.contains(userIds, UserContext.getUserId())){
throw new BusinessException("当前用户不能删除");
}
sysUserService.removeUsers(Arrays.asList(userIds));
return Result.succeed("ok");
}
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qkdata.biz.enums.SysConfigStatusEnum;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
@Data
@TableName("sys_config")
public class SysConfigPO extends BasePO {
private String paramKey;
private String paramValue;
private SysConfigStatusEnum status;
private String remark;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
@Data
@TableName("sys_log")
public class SysLogPO extends BasePO {
private String username;
private String operation;
private String method;
private String params;
private String ip;
private Long time;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qkdata.biz.enums.MenuTypeEnum;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
import java.util.List;
@Data
@TableName("sys_menu")
public class SysMenuPO extends BasePO {
private Long parentId;
/**
* 非数据库中字段
*/
@TableField(exist = false)
private String parentName;
private String name;
private String url;
private String perms;
private MenuTypeEnum type;
private String icon;
private Integer orderNum;
/**
* ztree属性
*/
@TableField(exist = false)
private Boolean open;
@TableField(exist = false)
private List<?> list;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_role_menu")
public class SysRoleMenuPO {
@TableId
private Long id;
private Long roleId;
private Long menuId;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
@Data
@TableName("sys_role")
public class SysRolePO extends BasePO {
private String name;
private String code;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qkdata.biz.enums.AccountStatusEnum;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName( "sys_user")
public class SysUserPO extends BasePO {
private String username;
private String password;
private String salt;
private String nickName;
private Date lastLoginTime;
private Date activateTime;
private AccountStatusEnum status;
@TableLogic
private Boolean isDel;
}
package com.qkdata.biz.sys.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_user_role")
public class SysUserRolePO {
@TableId
private Long id;
private Long userId;
private Long roleId;
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysConfigPO;
import com.qkdata.biz.sys.vo.QueryConfigModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysConfigMapper extends BaseMapper<SysConfigPO> {
List<SysConfigPO> queryPageList(Page<SysConfigPO> page, @Param("p") QueryConfigModel queryConfigModel);
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysLogPO;
import com.qkdata.biz.sys.vo.QueryLogModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysLogMapper extends BaseMapper<SysLogPO> {
List<SysLogPO> queryPageList(Page<SysLogPO> page, @Param("p") QueryLogModel queryLogModel);
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qkdata.biz.sys.entity.SysMenuPO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SysMenuMapper extends BaseMapper<SysMenuPO> {
List<SysMenuPO> queryNotButtonList();
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysRolePO;
import com.qkdata.biz.sys.vo.QueryRoleModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysRoleMapper extends BaseMapper<SysRolePO> {
List<SysRolePO> queryPageList(Page<SysRolePO> page, @Param("p") QueryRoleModel queryRoleModel);
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qkdata.biz.sys.entity.SysRoleMenuPO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenuPO> {
List<Long> selectMenuIdsByRoleId(Long roleId);
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.vo.QueryUserModel;
import com.qkdata.biz.sys.vo.SysUserModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysUserMapper extends BaseMapper<SysUserPO> {
List<String> queryAllPerms(Long userId);
List<SysUserModel> queryPageList(Page<SysUserModel> page, @Param("p") QueryUserModel queryUserModel);
List<Long> queryAllMenuId(Long userId);
}
package com.qkdata.biz.sys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qkdata.biz.sys.entity.SysUserRolePO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SysUserRoleMapper extends BaseMapper<SysUserRolePO> {
List<Long> queryRoleIdList(Long userId);
}
package com.qkdata.biz.sys.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import com.qkdata.biz.enums.AccountStatusEnum;
import com.qkdata.biz.sys.entity.SysMenuPO;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.vo.LoginUserInfo;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.jwt.JWTService;
import com.qkdata.common.oauth.AuthorizedUser;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.*;
@Service
public class ShiroService {
@Autowired
private SysUserService sysUserService;
@Autowired
private SysMenuService sysMenuService;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private JWTService jwtService;
public Set<String> getUserPermissions(Long userId) {
List<String> permsList;
//系统管理员,拥有最高权限
if(userId == 1){
List<SysMenuPO> menuList = sysMenuService.list();
permsList = new ArrayList<>(menuList.size());
for(SysMenuPO menu : menuList){
permsList.add(menu.getPerms());
}
}else{
permsList = sysUserService.queryAllPerms(userId);
}
//用户权限列表
Set<String> permsSet = new HashSet<>();
for(String perms : permsList){
if(StringUtils.isEmpty(perms)){
continue;
}
permsSet.addAll(Arrays.asList(perms.trim().split(",")));
}
return permsSet;
}
public LoginUserInfo login(String username, String password) throws JsonProcessingException {
SysUserPO userPO = getUserByUserName(username);
if(userPO == null || !userPO.getPassword().equals(new Sha256Hash(password, userPO.getSalt()).toHex())) {
throw new BusinessException("用户名或密码错误");
}
if (userPO.getStatus() == AccountStatusEnum.DISABLE){
throw new BusinessException("帐号已禁用");
}
String token = generatorToken(userPO);
LoginUserInfo loginUser = new LoginUserInfo();
BeanUtils.copyProperties(userPO,loginUser);
loginUser.setAuthorization(token);
return loginUser;
}
private String generatorToken(SysUserPO userPO) throws JsonProcessingException {
AuthorizedUser user = new AuthorizedUser();
user.setUserId(userPO.getId());
user.setUsername(userPO.getUsername());
String userJson = objectMapper.writeValueAsString(user);
Map<String,Object> userClaim = Maps.newConcurrentMap();
userClaim.put("user",userJson);
return jwtService.createJWT(userClaim);
}
public SysUserPO getUserByUserName(String username) {
return sysUserService.getByUsername(username);
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysConfigPO;
import com.qkdata.biz.sys.mapper.SysConfigMapper;
import com.qkdata.biz.sys.vo.QueryConfigModel;
import com.qkdata.common.base.enums.CodeEnum;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SysConfigService extends BaseServiceImpl<SysConfigMapper, SysConfigPO> {
public PageResult<SysConfigPO> queryPageList(QueryConfigModel queryConfigModel) {
Page<SysConfigPO> page = new Page<>(queryConfigModel.getPageIndex(),queryConfigModel.getPageSize());
List<SysConfigPO> pageList = baseMapper.queryPageList(page,queryConfigModel);
return PageResult.<SysConfigPO>builder().code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).data(pageList).build();
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysLogPO;
import com.qkdata.biz.sys.mapper.SysLogMapper;
import com.qkdata.biz.sys.vo.QueryLogModel;
import com.qkdata.common.base.enums.CodeEnum;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SysLogService extends BaseServiceImpl<SysLogMapper, SysLogPO> {
public PageResult<SysLogPO> queryPageList(QueryLogModel queryLogModel) {
Page<SysLogPO> page = new Page<>(queryLogModel.getPageIndex(),queryLogModel.getPageSize());
List<SysLogPO> pageList = baseMapper.queryPageList(page,queryLogModel);
return PageResult.<SysLogPO>builder().code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).data(pageList).build();
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.qkdata.biz.enums.MenuTypeEnum;
import com.qkdata.biz.sys.entity.SysMenuPO;
import com.qkdata.biz.sys.mapper.SysMenuMapper;
import com.qkdata.biz.sys.mapper.SysUserMapper;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import com.qkdata.common.constants.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class SysMenuService extends BaseServiceImpl<SysMenuMapper, SysMenuPO> {
@Autowired
private SysUserMapper sysUserMapper;
public List<SysMenuPO> getUserMenuList(Long userId) {
List<SysMenuPO> userMenuList = Lists.newArrayList();
//系统管理员,拥有最高权限
if (userId == Constants.SYS_ADMIN_ID){
userMenuList = getAllMenuList(null);
}else {
//用户菜单列表
List<Long> menuIdList = sysUserMapper.queryAllMenuId(userId);
userMenuList = getAllMenuList(menuIdList);
}
return userMenuList;
}
private List<SysMenuPO> getAllMenuList(List<Long> menuIdList) {
//查询根菜单列表
List<SysMenuPO> menuList = queryListParentId(0L, menuIdList);
//递归获取子菜单
getMenuTreeList(menuList, menuIdList);
return menuList;
}
private List<SysMenuPO> getMenuTreeList(List<SysMenuPO> menuList, List<Long> menuIdList) {
List<SysMenuPO> subMenuList = Lists.newArrayList();
for(SysMenuPO entity : menuList){
//目录
if(entity.getType() == MenuTypeEnum.FOLDER){
entity.setList(getMenuTreeList(queryListParentId(entity.getId(), menuIdList), menuIdList));
}
subMenuList.add(entity);
}
return subMenuList;
}
private List<SysMenuPO> queryListParentId(Long parentId, List<Long> menuIdList) {
List<SysMenuPO> menuList = queryListParentId(parentId);
if(menuIdList == null){
return menuList;
}
List<SysMenuPO> userMenuList = new ArrayList<>();
for(SysMenuPO menu : menuList){
if(menuIdList.contains(menu.getId())){
userMenuList.add(menu);
}
}
return userMenuList;
}
public List<SysMenuPO> queryListParentId(Long parentId) {
return list(Wrappers.<SysMenuPO>lambdaQuery().eq(SysMenuPO::getParentId,parentId));
}
public List<SysMenuPO> queryNotButtonList() {
return baseMapper.queryNotButtonList();
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.qkdata.biz.sys.entity.SysRoleMenuPO;
import com.qkdata.biz.sys.mapper.SysRoleMenuMapper;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service
public class SysRoleMenuService extends BaseServiceImpl<SysRoleMenuMapper, SysRoleMenuPO> {
public List<Long> queryMenuIdList(Long roleId) {
return baseMapper.selectMenuIdsByRoleId(roleId);
}
public void saveOrUpdateRoleMenu(Long roleId, List<Long> menuIdList) {
deleteByRoleIds(new Long[]{roleId});
if (CollectionUtils.isEmpty(menuIdList)){
return;
}
for (Long menuId : menuIdList){
SysRoleMenuPO po = new SysRoleMenuPO();
po.setRoleId(roleId);
po.setMenuId(menuId);
save(po);
}
}
private void deleteByRoleIds(Long[] roleIds) {
baseMapper.delete(Wrappers.<SysRoleMenuPO>lambdaQuery().in(SysRoleMenuPO::getRoleId,roleIds));
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysRolePO;
import com.qkdata.biz.sys.mapper.SysRoleMapper;
import com.qkdata.biz.sys.vo.QueryRoleModel;
import com.qkdata.biz.sys.vo.SysRoleModel;
import com.qkdata.common.base.enums.CodeEnum;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class SysRoleService extends BaseServiceImpl<SysRoleMapper, SysRolePO> {
@Autowired
private SysRoleMenuService sysRoleMenuService;
public PageResult<SysRolePO> queryPageList(QueryRoleModel queryRoleModel) {
Page<SysRolePO> page = new Page<>(queryRoleModel.getPageIndex(),queryRoleModel.getPageSize());
List<SysRolePO> pageList = baseMapper.queryPageList(page,queryRoleModel);
return PageResult.<SysRolePO>builder().code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).data(pageList).build();
}
public SysRoleModel getRoleInfo(Long id) {
SysRolePO po = getById(id);
if (po == null){
throw new BusinessException("请求错误,未找到对应角色");
}
List<Long> menuIds = sysRoleMenuService.queryMenuIdList(po.getId());
SysRoleModel model = new SysRoleModel();
BeanUtils.copyProperties(po,model);
model.setMenuIdList(menuIds);
return model;
}
@Transactional
public void saveRole(SysRoleModel sysRoleModel) {
SysRolePO po = new SysRolePO();
BeanUtils.copyProperties(sysRoleModel,po);
save(po);
sysRoleMenuService.saveOrUpdateRoleMenu(po.getId(),sysRoleModel.getMenuIdList());
}
@Transactional
public void updateRole(SysRoleModel sysRoleModel) {
SysRolePO po = getById(sysRoleModel.getId());
if (po == null){
throw new BusinessException("请求错误,角色不存在");
}
po.setName(sysRoleModel.getName());
po.setCode(sysRoleModel.getCode());
updateById(po);
sysRoleMenuService.saveOrUpdateRoleMenu(po.getId(),sysRoleModel.getMenuIdList());
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.qkdata.biz.sys.entity.SysUserRolePO;
import com.qkdata.biz.sys.mapper.SysUserRoleMapper;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service
public class SysUserRoleService extends BaseServiceImpl<SysUserRoleMapper, SysUserRolePO> {
public List<Long> queryRoleIdList(Long userId) {
return baseMapper.queryRoleIdList(userId);
}
public void saveOrUpdateUserRole(Long userId, List<Long> roleIdList) {
if (!CollectionUtils.isEmpty(roleIdList)){
//先删除用户与角色关系
deleteByUserId(userId);
//保存用户角色关系
for (Long roleId : roleIdList){
SysUserRolePO po = new SysUserRolePO();
po.setUserId(userId);
po.setRoleId(roleId);
baseMapper.insert(po);
}
}
}
public void deleteByUserId(Long userId){
baseMapper.delete(Wrappers.<SysUserRolePO>lambdaQuery().eq(SysUserRolePO::getUserId,userId));
}
public void deleteByUserIds(List<Long> userIds) {
baseMapper.delete(Wrappers.<SysUserRolePO>lambdaQuery().in(SysUserRolePO::getUserId,userIds));
}
}
package com.qkdata.biz.sys.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.mapper.SysUserMapper;
import com.qkdata.biz.sys.vo.QueryUserModel;
import com.qkdata.biz.sys.vo.SysUserModel;
import com.qkdata.common.base.enums.CodeEnum;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.PageResult;
import com.qkdata.common.base.service.impl.BaseServiceImpl;
import com.qkdata.common.util.UserContext;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.List;
@Service
public class SysUserService extends BaseServiceImpl<SysUserMapper, SysUserPO> {
@Autowired
private SysUserRoleService sysUserRoleService;
public SysUserPO getByUsername(String username) {
return baseMapper.selectOne(Wrappers.<SysUserPO>lambdaQuery().eq(SysUserPO::getUsername,username));
}
public List<String> queryAllPerms(Long userId) {
return baseMapper.queryAllPerms(userId);
}
public PageResult<SysUserModel> queryPageList(QueryUserModel queryUserModel) {
Page<SysUserModel> page = new Page<>(queryUserModel.getPageIndex(),queryUserModel.getPageSize());
List<SysUserModel> pageList = baseMapper.queryPageList(page,queryUserModel);
return PageResult.<SysUserModel>builder().code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).data(pageList).build();
}
public List<Long> queryRoleIdList(Long userId) {
return sysUserRoleService.queryRoleIdList(userId);
}
@Transactional
public void saveUser(SysUserModel sysUserModel) {
SysUserPO po = getByUsername(sysUserModel.getUsername());
if (po != null){
throw new BusinessException("帐号已存在");
}
po = new SysUserPO();
BeanUtils.copyProperties(sysUserModel,po);
po.setSalt(RandomStringUtils.randomAlphanumeric(20));
po.setPassword(new Sha256Hash(sysUserModel.getPassword(), po.getSalt()).toHex());
save(po);
sysUserRoleService.saveOrUpdateUserRole(po.getId(),sysUserModel.getRoleIdList());
}
@Transactional
public void updateUser(SysUserModel sysUserModel) {
SysUserPO po = getById(sysUserModel.getId());
if (po == null){
throw new BusinessException("请求错误,用户不存在");
}
po.setUsername(sysUserModel.getUsername());
if (!StringUtils.isEmpty(sysUserModel.getPassword())){
po.setPassword(new Sha256Hash(sysUserModel.getPassword(),po.getSalt()).toHex());
}
po.setStatus(sysUserModel.getStatus());
updateById(po);
sysUserRoleService.saveOrUpdateUserRole(po.getId(),sysUserModel.getRoleIdList());
}
public void updatePassword(SysUserPO user, String password, String newPassword) {
//sha256加密
String pwd = new Sha256Hash(password, UserContext.getUser().getSalt()).toHex();
if (!user.getPassword().equals(pwd)){
throw new BusinessException("密码错误");
}
//sha256加密
String newPwd = new Sha256Hash(newPassword, UserContext.getUser().getSalt()).toHex();
user.setPassword(newPwd);
updateById(user);
}
@Transactional
public void removeUsers(List<Long> ids) {
removeByIds(ids);
sysUserRoleService.deleteByUserIds(ids);
}
}
package com.qkdata.biz.sys.vo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class LoginModel {
@NotBlank(message = "请填写登陆帐号")
private String username;
@NotBlank(message = "请填写密码")
private String password;
}
package com.qkdata.biz.sys.vo;
import lombok.Data;
@Data
public class LoginUserInfo {
private Long id;
private String username;
private String email;
private String mobile;
private String authorization;
}
package com.qkdata.biz.sys.vo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class PasswordModel {
@NotBlank(message = "原密码不能为空")
private String password;
@NotBlank(message = "新密码不能为空")
private String newPassword;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.common.constants.Constants;
import lombok.Data;
@Data
public class QueryConfigModel {
private Integer pageIndex = Constants.DEFAULT_PAGE;
private Integer pageSize = Constants.DEFAULT_PAGE_SIZE;
private String paramKey;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.common.constants.Constants;
import lombok.Data;
@Data
public class QueryLogModel {
private Integer pageIndex = Constants.DEFAULT_PAGE;
private Integer pageSize = Constants.DEFAULT_PAGE_SIZE;
private String condition;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.common.constants.Constants;
import lombok.Data;
@Data
public class QueryRoleModel {
private Integer pageIndex = Constants.DEFAULT_PAGE;
private Integer pageSize = Constants.DEFAULT_PAGE_SIZE;
private String name;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.common.constants.Constants;
import lombok.Data;
@Data
public class QueryUserModel {
private Integer pageIndex = Constants.DEFAULT_PAGE;
private Integer pageSize = Constants.DEFAULT_PAGE_SIZE;
private String username;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.biz.sys.entity.SysMenuPO;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
import java.util.Set;
@Data
@AllArgsConstructor
public class SysNavModel {
private List<SysMenuPO> menuList;
private Set<String> permissions;
}
package com.qkdata.biz.sys.vo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.List;
@Data
public class SysRoleModel {
private Long id;
@NotBlank(message = "角色名称不能为空")
private String name;
@NotBlank(message = "角色代码不能为空")
private String code;
private List<Long> menuIdList;
}
package com.qkdata.biz.sys.vo;
import com.qkdata.biz.enums.AccountStatusEnum;
import com.qkdata.common.constants.AddGroup;
import com.qkdata.common.constants.UpdateGroup;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.List;
@Data
public class SysUserModel {
private Long id;
@NotBlank(message = "用户名不能为空",groups = {AddGroup.class, UpdateGroup.class})
private String username;
@NotBlank(message = "密码不能为空",groups = AddGroup.class)
private String password;
private String email;
private String mobile;
private AccountStatusEnum status;
private List<Long> roleIdList;
}
package com.qkdata.common.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLimit {
/**
* 限制某时间段内可以访问的次数,默认设置200
*/
int limitCounts() default 200;
/**
* @return
* @Description: 限制访问的某一个时间段,单位为秒,默认值10秒
*/
int timeSecond() default 10;
}
package com.qkdata.common.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
boolean includeParam() default true;
}
package com.qkdata.common.aspect;
import com.qkdata.common.annotation.ApiLimit;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
@Slf4j
public class ApiLimitAspect {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Pointcut("@annotation(com.qkdata.common.annotation.ApiLimit)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
ApiLimit apiLimit = getAnnotation(point);
return dealLimit(point,apiLimit,false);
}
private Object dealLimit(ProceedingJoinPoint point,ApiLimit apiLimit,boolean flag) throws Throwable {
String msgKey = getMsgKey(point);
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
long methodCounts = valueOperations.increment(msgKey, 1);
// 如果该key不存在,则从0开始计算,并且当count为1的时候,设置过期时间
if (methodCounts == 1) {
redisTemplate.expire(msgKey, apiLimit.timeSecond(), TimeUnit.SECONDS);
}
// 如果redis中的count大于限制的次数,则等待10秒重试
if (methodCounts > apiLimit.limitCounts()) {
if (!flag) {
//等待10秒后,第一次重试
Thread.sleep(10 * 1000);
log.warn("等待10秒后,第一次重试...");
// 递归,再次请求业务方法
return dealLimit(point, apiLimit,true);
} else {
//如果第一次请求被限制了,等待10秒后重试,如果再次失败,则抛出异常
throw new BusinessException("请求超时");
}
}else {
return point.proceed();
}
}
private ApiLimit getAnnotation(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
return method.getAnnotation(ApiLimit.class);
}
private String getMsgKey(ProceedingJoinPoint point){
MethodSignature signature = (MethodSignature) point.getSignature();
String className = point.getTarget().getClass().getName();
String methodName = signature.getName();
String msgKey = className + "." + methodName;
return msgKey;
}
}
package com.qkdata.common.aspect;
import com.alibaba.fastjson.JSONArray;
import com.qkdata.biz.sys.entity.SysLogPO;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.service.SysLogService;
import com.qkdata.common.annotation.SysLog;
import com.qkdata.common.util.HttpContextUtils;
import com.qkdata.common.util.IPUtils;
import com.qkdata.common.util.UserContext;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut("@annotation(com.qkdata.common.annotation.SysLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = null;
try {
result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveSysLog(point, time);
return result;
}catch (Throwable e){
long time = System.currentTimeMillis() - beginTime;
saveSysLog(point,time);
throw e;
}
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLogPO sysLog = new SysLogPO();
SysLog syslog = method.getAnnotation(SysLog.class);
if(syslog != null){
//注解上的描述
sysLog.setOperation(syslog.value());
}
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
if (syslog != null && syslog.includeParam()){
//请求的参数
Object[] args = joinPoint.getArgs();
try{
String params = JSONArray.toJSONString(args);
sysLog.setParams(params);
}catch (Exception e){
}
}
sysLog.setTime(time);
//获取request
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
//设置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));
//用户名
SysUserPO user = UserContext.getUser();
if (user != null){
String username = UserContext.getUser().getUsername();
sysLog.setUsername(username);
}
//保存系统日志
sysLogService.save(sysLog);
}
}
package com.qkdata.common.base.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class BasePO<T extends Model<?>> extends Model<T> {
/**
* 主键ID
*/
@TableId
private Long id;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Override
protected Serializable pkVal() {
return this.id;
}
}
package com.qkdata.common.base.enums;
public enum CodeEnum {
SUCCESS(200),
ERROR(1000);
private Integer code;
CodeEnum(Integer code){
this.code = code;
}
public Integer getCode() {
return code;
}
}
package com.qkdata.common.base.exception;
import lombok.Data;
/**
* 业务异常
*/
@Data
public class BusinessException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 4650570427238258122L;
public BusinessException() {
super();
}
public BusinessException(String message) {
super(message);
}
public BusinessException(Throwable cause) {
super(cause);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
}
package com.qkdata.common.base.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 分页实体类
*
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PageResult<T> implements Serializable {
private static final long serialVersionUID = -275582248840137389L;
/**
* 总数
*/
private Long count;
/**
* 是否成功:0 成功、1 失败
*/
private int code;
/**
* 当前页结果集
*/
private List<T> data;
}
package com.qkdata.common.base.model;
import com.qkdata.common.base.enums.CodeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> implements Serializable {
private T datas;
private Integer resp_code;
private String resp_msg;
public static <T> Result<T> succeed(String msg) {
return of(null, CodeEnum.SUCCESS.getCode(), msg);
}
public static <T> Result<T> succeed(T model, String msg) {
return of(model, CodeEnum.SUCCESS.getCode(), msg);
}
public static <T> Result<T> succeed(T model) {
return of(model, CodeEnum.SUCCESS.getCode(), "");
}
public static <T> Result<T> of(T datas, Integer code, String msg) {
return new Result<>(datas, code, msg);
}
public static <T> Result<T> failed(String msg) {
return of(null, CodeEnum.ERROR.getCode(), msg);
}
public static <T> Result<T> failed(T model, String msg) {
return of(model, CodeEnum.ERROR.getCode(), msg);
}
}
package com.qkdata.common.base.service.impl;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
public abstract class BaseServiceImpl<M extends BaseMapper<T>,T> extends ServiceImpl<M,T> {
}
package com.qkdata.common.config;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.handlers.StrictFill;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.Date;
/**
* 自定义填充公共字段
*
*/
@Component
public class DateMetaObjectHandler implements MetaObjectHandler {
/**
* 插入填充,字段为空自动填充
*/
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, DateUtil.date());
this.strictUpdateFill(metaObject,"updateTime",Date.class,DateUtil.date());
}
/**
* 更新填充
*/
@Override
public void updateFill(MetaObject metaObject) {
setFieldValByName("updateTime", new Date(), metaObject);
}
}
\ No newline at end of file
package com.qkdata.common.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean shiroFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
//该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
registration.addInitParameter("targetFilterLifecycle", "true");
registration.setEnabled(true);
registration.setOrder(Integer.MAX_VALUE - 1);
registration.addUrlPatterns("/api/*");
return registration;
}
}
package com.qkdata.common.config;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.validation.ConstraintViolationException;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandle {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseEntity<Result> bindExceptionHandler(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
String message = bindingResult.getFieldError().getDefaultMessage();
log.warn("方法参数无效: {}", message);
return ResponseEntity.status(HttpStatus.OK).body(Result.failed(message));
}
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public ResponseEntity<Result> httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e) {
log.warn("不支持的Method: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Result.failed("不支持当前请求方法"));
}
@ExceptionHandler(value = Exception.class)
public ResponseEntity<Result> handler(Exception e) {
log.error("服务器错误", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.failed("服务器处理错误"));
}
@ExceptionHandler(value = BusinessException.class)
public ResponseEntity<Result> businessExceptionhandler(BusinessException e) {
log.warn("业务异常, message: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.OK).body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public ResponseEntity<Result> httpMessageConvertExceptionHanlder(HttpMessageNotReadableException e) {
log.warn("无法解析请求参数: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = MissingPathVariableException.class)
public ResponseEntity<Result> missingPathVariableExceptionHanlder(MissingPathVariableException e) {
log.warn("请求链接缺少参数: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = MissingServletRequestParameterException.class)
public ResponseEntity<Result> requestParameterExceptionHandler(MissingServletRequestParameterException e) {
log.warn("缺少必需的请求参数: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
public ResponseEntity<Result> methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException e) {
log.warn("参数类别转换失败: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = ConstraintViolationException.class)
public ResponseEntity<Result> constraintViolationExceptionHandler(ConstraintViolationException e) {
log.warn("参数校验异常: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
public ResponseEntity<Result> httpMediaTypeNotSupportedExceptionHandler(HttpMediaTypeNotSupportedException e) {
log.warn("不支持MediaType: {}", e.getMessage());
return ResponseEntity.badRequest().body(Result.failed(e.getMessage()));
}
@ExceptionHandler(value = NoHandlerFoundException.class)
public ResponseEntity<Result> noHandlerFoundExceptionHandler(NoHandlerFoundException e) {
log.warn("no handler found: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Result.failed(e.getMessage()));
}
}
package com.qkdata.common.config;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
import java.nio.charset.Charset;
public class HttpHeaderInterceptor implements ClientHttpRequestInterceptor {
private final String name;
private final String value;
public HttpHeaderInterceptor(String name, String value) {
// Assert.hasLength(name, "Name must not be empty");
// Assert.hasLength(value, "Value" + " must not be empty");
this.name = name;
this.value = value;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
request.getHeaders().add(this.name, this.value);
charsetToUTF8(request);
return execution.execute(request, body);
}
private void charsetToUTF8(HttpRequest request) {
MediaType contentType = request.getHeaders().getContentType();
if(contentType.getCharset() == null || Charset.forName("UTF-8") != contentType.getCharset()) {
contentType = new MediaType(contentType, Charset.forName("UTF-8"));
request.getHeaders().setContentType(contentType);
}
}
}
package com.qkdata.common.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qkdata.common.trace.HttpTraceLogFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnWebApplication
public class HttpTraceConfiguration {
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
static class ServletTraceFilterConfiguration {
@Bean
@ConditionalOnMissingBean
public HttpTraceLogFilter httpTraceLogFilter(ObjectMapper objectMapper) {
return new HttpTraceLogFilter(objectMapper);
}
}
}
package com.qkdata.common.config;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.qkdata.common.converter.*;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
@Configuration
public class JacksonConfiguration {
@Bean
public Jackson2ObjectMapperBuilderCustomizer objectMapper() {
return builder -> builder.failOnEmptyBeans(false)
.failOnUnknownProperties(false)
.createXmlMapper(false)
// .serializationInclusion(JsonInclude.Include.NON_EMPTY)
// .serializationInclusion(JsonInclude.Include.NON_NULL)
.serializerByType(Date.class,new DateSerializer())
.deserializerByType(Date.class,new DateDeserializer())
.serializerByType(LocalDate.class, new LocalDateSerializer())
.deserializerByType(LocalDate.class, new LocalDateDeserializer())
.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer())
.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer())
.featuresToDisable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
}
package com.qkdata.common.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
@Slf4j
public class LogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
log.info("restTemplate请求日志, uri: {}, method: {}, methodValue: {}, headers: {}", request.getURI(),
request.getMethod(), request.getMethodValue(), request.getHeaders());
return execution.execute(request, body);
}
}
package com.qkdata.common.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mybatis-plus自动配置
*
*/
@Configuration
public class MybatisPlusAutoConfigure {
/**
* 分页插件,自动识别数据库类型
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
package com.qkdata.common.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.qkdata.common.converter.LocalDateTimeDeserializer;
import com.qkdata.common.converter.LocalDateTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.time.LocalDateTime;
@Configuration
@Slf4j
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(5 * 60))
.disableCachingNullValues();
return RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter
(redisConnectionFactory)).cacheDefaults(defaultCacheConfig).transactionAware().build();
}
@Bean(value = "stringRedisTemplate")
public RedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
@Bean(value = "objectRedisTemplate")
public RedisTemplate objectRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
SimpleModule module = new SimpleModule();
module.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer());
module.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer());
mapper.registerModule(module);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//订阅了一个叫chat 的通道
// container.addMessageListener(syncFriendListener(), new PatternTopic(Constants.topic_sync_friend));
// container.addMessageListener(syncRoomListener(),new PatternTopic(Constants.topic_sync_room));
// container.addMessageListener(syncMessageListener(),new PatternTopic(Constants.topic_sync_message));
//这个container 可以添加多个 messageListener
return container;
}
// @Bean
// public MessageListener syncMessageListener() {
//
// return new WxMessageListener();
// }
//
// @Bean
// public MessageListener syncRoomListener() {
// return new SyncRoomListener();
// }
//
// @Bean
// public MessageListener syncFriendListener() {
// return new SyncFriendListener();
// }
}
package com.qkdata.common.config;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
@Configuration
public class RestTemplateConfig {
@Bean
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.getMessageConverters().set(1,
new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(10000);//单位为ms
factory.setConnectTimeout(5000);//单位为ms
return factory;
}
}
\ No newline at end of file
package com.qkdata.common.config;
import com.qkdata.common.jwt.JWTProperties;
import com.qkdata.common.oauth.OAuthFilter;
import com.qkdata.common.oauth.OAuthRealm;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
@EnableConfigurationProperties(JWTProperties.class)
public class ShiroConfig {
@Autowired
private JWTProperties jwtProperties;
@Bean("securityManager")
public SecurityManager securityManager(OAuthRealm oAuth2Realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRememberMeManager(null);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
securityManager.setRealm(oAuth2Realm);
return securityManager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
//oauth过滤
Map<String, Filter> filters = new HashMap<>();
filters.put("oauth", new OAuthFilter());
shiroFilter.setFilters(filters);
Map<String, String> filterMap = new LinkedHashMap<>();
for (String url : jwtProperties.getExcludeUrls()){
filterMap.put(url,"anon");
}
for (String url : jwtProperties.getIncludeUrls()){
filterMap.put(url, "oauth");
}
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
// @Bean("lifecycleBeanPostProcessor")
// public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
// return new LifecycleBeanPostProcessor();
// }
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
package com.qkdata.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableSwagger2
public class Swagger2Config {
/**
* @return
*/
@Bean
public Docket createRestApi() {
// ParameterBuilder ticketPar = new ParameterBuilder();
// List<Parameter> pars = new ArrayList<>();
// ticketPar.name(HttpHeaders.AUTHORIZATION).description("user token")
// .modelRef(new ModelRef("string")).parameterType("header")
// .required(false).build();
// pars.add(ticketPar.build());
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.basePackage("com.qkdata"))
.paths(PathSelectors.any()).build()
.securitySchemes(securitySchemes()).securityContexts(securityContexts());
// .globalOperationParameters(pars);
}
private List<ApiKey> securitySchemes() {
List<ApiKey> apiKeys = new ArrayList<>(1);
ApiKey apiKey = new ApiKey("Authorization","Authorization","header");
apiKeys.add(apiKey);
return apiKeys;
}
private List<SecurityContext> securityContexts() {
List<SecurityContext> contexts = new ArrayList<>(1);
SecurityContext securityContext = SecurityContext.builder()
.securityReferences(defaultAuth())
//.forPaths(PathSelectors.regex("^(?!auth).*$"))
.build();
contexts.add(securityContext);
return contexts;
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> references = new ArrayList<>(1);
references.add(new SecurityReference("Authorization", authorizationScopes));
return references;
}
/**
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("backend service API").version("1.0").build();
}
}
package com.qkdata.common.config;
import com.qkdata.common.converter.StringToDateConverter;
import com.qkdata.common.converter.StringToLocalDateConverter;
import com.qkdata.common.converter.StringToLocalDateTimeConverter;
import com.qkdata.common.trace.TraceIdInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedOrigins("*")
.allowCredentials(true)
.maxAge(1800);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(stringToLocalDateConverter());
registry.addConverter(stringToLocalDateTimeConverter());
registry.addConverter(stringToDateConverter());
}
private Converter stringToDateConverter() {
return new StringToDateConverter();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(traceIdInterceptor());
}
private HandlerInterceptor traceIdInterceptor() {
return new TraceIdInterceptor();
}
private Converter stringToLocalDateConverter() {
return new StringToLocalDateConverter();
}
private Converter stringToLocalDateTimeConverter() {
return new StringToLocalDateTimeConverter();
}
}
package com.qkdata.common.constants;
public interface AddGroup {
}
package com.qkdata.common.constants;
public final class Constants {
public static final String PSAAWORD = "123456";
public static final Long SYS_ADMIN_ID = 1L;
public static final Integer DEFAULT_PAGE = 1;
public static final Integer DEFAULT_PAGE_SIZE = 10;
}
package com.qkdata.common.constants;
public final class DateTimeFormatterPattern {
private DateTimeFormatterPattern() {
}
public static final String YYYY_MM_DD = "yyyy-MM-dd";
public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
}
package com.qkdata.common.constants;
/**
* 分页常量
*/
public final class Page {
private Page() {
}
public static final String CURRENT_PAGE_STR = "1";
public static final Integer CURRENT_PAGE_INT = 1;
public static final String PAGE_SIZE_STR = "10";
public static final Integer PAGE_SIZE_INT = 10;
}
package com.qkdata.common.constants;
public interface UpdateGroup {
}
package com.qkdata.common.constants;
public final class ValidatorPattern {
private ValidatorPattern(){}
public static final String MOBILE_PATTERN = "^(13|14|15|16|17|18|19)[0-9]{9}$";
}
package com.qkdata.common.converter;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.Date;
/**
* 用于@RequestBody中Date类型参数序列化与反序列化
*/
@Slf4j
public class DateDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
try {
return DateUtil.parseDateTime(p.getValueAsString());
} catch (Exception e) {
log.warn("时间转换异常", e);
throw new BusinessException("时间转换异常");
}
}
}
package com.qkdata.common.converter;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.Date;
/**
* 用于@RequestBody中Date类型参数序列化与反序列化
*/
public class DateSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
if (value == null) {
return;
}
gen.writeString(DateUtil.formatDateTime(value));
}
}
package com.qkdata.common.converter;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
/**
* 用于@RequestBody中LocalDate类型参数序列化与反序列化
*/
@Slf4j
public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
@Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
try {
return Instant.ofEpochMilli(p.getLongValue()).atZone(ZoneId.systemDefault()).toLocalDate();
} catch (Exception e) {
log.warn("时间转换异常", e);
throw new BusinessException("时间转换异常");
}
}
}
package com.qkdata.common.converter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneId;
/**
* 用于@RequestBody中LocalDate类型参数序列化与反序列化
*/
public class LocalDateSerializer extends JsonSerializer<LocalDate> {
@Override
public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
return;
}
gen.writeNumber(value.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli());
}
}
package com.qkdata.common.converter;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* 用于@RequestBody中LocalDateTime参数序列化与反序列化
*/
@Slf4j
public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
try {
return Instant.ofEpochMilli(p.getLongValue()).atZone(ZoneId.systemDefault()).toLocalDateTime();
} catch (Exception e) {
log.warn("时间转换异常", e);
throw new BusinessException("时间转换异常");
}
}
}
package com.qkdata.common.converter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
/**
* 用于@RequestBody中LocalDateTime参数序列化与反序列化
*/
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
return;
}
gen.writeNumber(value.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
}
}
package com.qkdata.common.converter;
import cn.hutool.core.date.DateUtil;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
import java.util.Date;
/**
* 用于QUERY参数转换成Date
*/
@Slf4j
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
try {
return DateUtil.parseDateTime(source);
} catch (Exception e) {
log.warn("时间转换异常", e);
throw new BusinessException("时间转换异常");
}
}
}
package com.qkdata.common.converter;
import com.qkdata.common.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
/**
* 用于QUERY参数转换成LocalDate
*/
@Slf4j
public class StringToLocalDateConverter implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
try {
return Instant.ofEpochMilli(Long.valueOf(source)).atZone(ZoneId.systemDefault()).toLocalDate();
} catch (Exception e) {
log.warn("时间转换异常", e);
throw new BusinessException("时间转换异常");
}
}
}
package com.qkdata.common.converter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* 用于QUERY参数转换成LocalDateTime
*/
public class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
return Instant.ofEpochMilli(Long.valueOf(source)).atZone(ZoneId.systemDefault()).toLocalDateTime();
}
}
package com.qkdata.common.jwt;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Set;
@Data
@ConfigurationProperties(prefix = "jwt")
public class JWTProperties {
private String typ = "JWT";
private String alg = "HS256";
private String secret;
private String iss = "qkdata";
private String aud;
private Integer exp = 2;
/**
* 是否启用拦截器
*/
private Boolean enableInterceptor = Boolean.FALSE;
/**
* 需要拦截的URL
*/
private Set<String> includeUrls;
/**
* 需要排除的URL
*/
private Set<String> excludeUrls;
}
package com.qkdata.common.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
@EnableConfigurationProperties(JWTProperties.class)
public class JWTService {
@Autowired
private JWTProperties jwtProperties;
/**
* 头部信息
*
* @return
*/
private void buildHeader(JWTCreator.Builder builder) {
Map<String, Object> header = new HashMap<>();
header.put("typ", jwtProperties.getTyp());
header.put("alg", jwtProperties.getAlg());
builder.withHeader(header);
}
/**
* 载荷信息
*/
private void buildPayload(JWTCreator.Builder builder, Map<String, Object> claims) {
LocalDateTime now = LocalDateTime.now();
builder.withIssuer(jwtProperties.getIss())
.withAudience(jwtProperties.getAud())
.withIssuedAt(issuedAt((now)))
.withExpiresAt(expiresAt(now, jwtProperties.getExp()));
if (claims != null) {
for (Map.Entry<String, Object> entry : claims.entrySet()) {
Object value = entry.getValue();
if (value instanceof Boolean) {
builder.withClaim(entry.getKey(), (Boolean) value);
} else if (value instanceof Integer) {
builder.withClaim(entry.getKey(), (Integer) value);
} else if (value instanceof Long) {
builder.withClaim(entry.getKey(), (Long) value);
} else if (value instanceof String) {
builder.withClaim(entry.getKey(), (String) value);
} else if (value instanceof Double) {
builder.withClaim(entry.getKey(), (Double) value);
} else if (value instanceof Date) {
builder.withClaim(entry.getKey(), (Date) value);
} else {
throw new IllegalStateException("jwt payload claims type mismatch");
}
}
}
}
/**
* 签名
*
* @param builder
* @return jwt字符串
*/
private String buildSignature(JWTCreator.Builder builder) {
return builder.sign(Algorithm.HMAC256(jwtProperties.getSecret()));
}
/**
* 创建JWT
*
* @param claims 业务参数
* @return
*/
public String createJWT(Map<String, Object> claims) {
JWTCreator.Builder builder = JWT.create();
buildHeader(builder);
buildPayload(builder, claims);
return buildSignature(builder);
}
public String createJWT() {
return createJWT(null);
}
/**
* 签发时间
*
* @param issuedAt
* @return
*/
private Date issuedAt(LocalDateTime issuedAt) {
return Date.from(issuedAt.atZone(ZoneId.systemDefault()).toInstant());
}
/**
* 过期时间
*
* @param issuedAt
* @param hours
* @return
*/
private Date expiresAt(LocalDateTime issuedAt, Integer hours) {
return Date.from(issuedAt.plusHours(hours).atZone(ZoneId.systemDefault()).toInstant());
}
/**
* 解码
*
* @param jwt
*/
public DecodedJWT decode(String jwt) {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(jwtProperties.getSecret())).build();
return verifier.verify(jwt);
}
}
package com.qkdata.common.oauth;
public enum AuthorizationResponseEnum{
MISSING_TOKEN("403", "HEADER中不存在TOKEN"),
INVALID_TOKEN("403", "校验TOKEN失败"),
EXPIRED_TOKEN("403", "TOKEN已过期"),
MISSING_CLAIMS("403", "不合法的TOKEN, 信息可能被篡改"),
INVALID_CLAIM("403", "不合法的TOKEN, 系统中不存在资源");
private String value;
private String text;
AuthorizationResponseEnum(String value, String text) {
this.value = value;
this.text = text;
}
public String value() {
return this.value;
}
public String text() {
return this.text;
}
}
package com.qkdata.common.oauth;
import lombok.Data;
@Data
public class AuthorizedUser {
private Long userId;
private String username;
}
package com.qkdata.common.oauth;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qkdata.common.base.model.Result;
import com.qkdata.common.util.HttpContextUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class OAuthFilter extends AuthenticatingFilter {
private ObjectMapper mapper = new ObjectMapper();
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
//获取请求token
String token = getRequestToken((HttpServletRequest) request);
if(StringUtils.isEmpty(token)){
return null;
}
return new OAuthToken(token);
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){
return true;
}
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//获取请求token,如果token不存在,直接返回403
String token = getRequestToken((HttpServletRequest) request);
if(StringUtils.isEmpty(token)){
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setContentType("application/json;charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
Result responseData = Result.failed(AuthorizationResponseEnum.MISSING_TOKEN.text());
String json = mapper.writeValueAsString(responseData);
httpResponse.getWriter().print(json);
return false;
}
return executeLogin(request, response);
}
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setContentType("application/json;charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
try {
//处理登录失败的异常
Throwable throwable = e.getCause() == null ? e : e.getCause();
Result rd = Result.failed(AuthorizationResponseEnum.INVALID_TOKEN.value(),e.getMessage());
String json = mapper.writeValueAsString(rd);
httpResponse.getWriter().print(json);
} catch (IOException e1) {
}
return false;
}
/**
* 获取请求的token
*/
private String getRequestToken(HttpServletRequest httpRequest){
//从header中获取token
String token = httpRequest.getHeader(HttpHeaders.AUTHORIZATION);
//如果header中不存在token,则从参数中获取token
if(StringUtils.isEmpty(token)){
token = httpRequest.getParameter(HttpHeaders.AUTHORIZATION);
}
return token;
}
}
package com.qkdata.common.oauth;
import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.enums.AccountStatusEnum;
import com.qkdata.biz.sys.service.ShiroService;
import com.qkdata.common.jwt.JWTService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
@Slf4j
@Component
public class OAuthRealm extends AuthorizingRealm {
@Autowired
private ShiroService shiroService;
@Autowired
private JWTService jwtService;
private ObjectMapper mapper = new ObjectMapper();
private DecodedJWT decodedJWT;
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof OAuthToken;
}
/**
* 授权(验证权限时调用)
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SysUserPO user = (SysUserPO) principals.getPrimaryPrincipal();
Long userId = user.getId();
//用户权限列表
Set<String> permsSet = new HashSet<>();
permsSet.add("all");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permsSet);
return info;
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String accessToken = (String) token.getPrincipal();
if (accessToken == null) {
throw new AuthenticationException(AuthorizationResponseEnum.MISSING_TOKEN.text());
}
try {
decodedJWT = jwtService.decode(accessToken);
String userJson = decodedJWT.getClaim("user").asString();
AuthorizedUser authUser = mapper.readValue(userJson,AuthorizedUser.class);
SysUserPO user = shiroService.getUserByUserName(authUser.getUsername());
if (user == null){
throw new AuthenticationException(AuthorizationResponseEnum.INVALID_CLAIM.text());
}
if (user.getStatus() == AccountStatusEnum.DISABLE){
throw new AuthenticationException("您已退出团队,不能登陆");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, getName());
return info;
} catch (JWTDecodeException | AlgorithmMismatchException | SignatureVerificationException | InvalidClaimException e) {
log.warn("校验TOKEN失败: {}, token: {}", e.getMessage(), token);
throw new AuthenticationException(AuthorizationResponseEnum.INVALID_TOKEN.text());
} catch (TokenExpiredException e) {
log.warn("TOKEN已过期: {}, token: {}", e.getMessage(), token);
throw new AuthenticationException(AuthorizationResponseEnum.EXPIRED_TOKEN.text());
} catch (IOException e) {
throw new AuthenticationException(AuthorizationResponseEnum.INVALID_CLAIM.text());
}
}
}
package com.qkdata.common.oauth;
import org.apache.shiro.authc.AuthenticationToken;
public class OAuthToken implements AuthenticationToken {
private String token;
public OAuthToken(String token){
this.token = token;
}
@Override
public String getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}
package com.qkdata.common.trace;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* 记录请求参数、请求路径、响应数据、请求时间等信息
*
* @see org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter
*/
@Slf4j
public class HttpTraceLogFilter extends OncePerRequestFilter implements Ordered {
private int order = Ordered.LOWEST_PRECEDENCE - 10;
private ObjectMapper objectMapper;
public HttpTraceLogFilter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public int getOrder() {
return this.order;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (!isRequestValid(request)) {
filterChain.doFilter(request, response);
return;
}
// request的stream只能被消费一次
if (!(request instanceof ContentCachingRequestWrapper)) {
request = new ContentCachingRequestWrapper(request);
}
if (!(response instanceof ContentCachingResponseWrapper)) {
response = new ContentCachingResponseWrapper(response);
}
int status = HttpStatus.OK.value();
long startTime = Instant.now().toEpochMilli();
try {
filterChain.doFilter(request, response);
status = response.getStatus();
} finally {
long timeTaken = Instant.now().toEpochMilli() - startTime;
TraceLog traceLog = new TraceLog();
traceLog.setTraceId(MDC.get(TraceIdInterceptor.TRACE_ID_KEY));
traceLog.setTimeTaken(timeTaken);
traceLog.setCreateAt(LocalDateTime.now());
traceLog.setMethod(request.getMethod());
traceLog.setPath(request.getRequestURI());
traceLog.setStatus(status);
traceLog.setRequestBody(getRequestBody(request));
traceLog.setResponseBody(getResponseBody(response));
traceLog.setParameters(objectMapper.writeValueAsString(request.getParameterMap()));
traceLog.setHeaders(objectMapper.writeValueAsString(getHeaders(request)));
// log.info("Http Trace Log: {}", objectMapper.writeValueAsString(traceLog));
// 复原response,正常的返回数据
updateResponse(response);
MDC.remove(TraceIdInterceptor.TRACE_ID_KEY);
}
}
private Map<String, Object> getHeaders(HttpServletRequest request) {
Enumeration<String> headerNames = request.getHeaderNames();
Map<String, Object> heads = new HashMap<>();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
heads.put(name, value);
}
return heads;
}
private boolean isRequestValid(HttpServletRequest request) {
try {
new URI(request.getRequestURL().toString());
return true;
} catch (URISyntaxException ex) {
return false;
}
}
private String getRequestBody(HttpServletRequest request) {
String requestBody = "";
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (wrapper != null) {
try {
requestBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
} catch (IOException e) {
// NOOP
}
}
return requestBody;
}
private String getResponseBody(HttpServletResponse response) {
String responseBody = "";
ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
if (wrapper != null) {
try {
responseBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
} catch (IOException e) {
// NOOP
}
}
return responseBody;
}
private void updateResponse(HttpServletResponse response) throws IOException {
ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
Objects.requireNonNull(responseWrapper).copyBodyToResponse();
}
}
package com.qkdata.common.trace;
import org.slf4j.MDC;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.Map;
/**
* 这是{@link ThreadPoolTaskExecutor}的一个简单替换,可以在每个任务之前设置子线程的MDC数据。
* <p/>
* 在记录日志的时候,一般情况下我们会使用MDC来存储每个线程的特有参数,如身份信息等,以便更好的查询日志。
* 但是Logback在最新的版本中因为性能问题,不会自动的将MDC的内存传给子线程。所以Logback建议在执行异步线程前
* 先通过MDC.getCopyOfContextMap()方法将MDC内存获取出来,再传给线程。
* 并在子线程的执行的最开始调用MDC.setContextMap(context)方法将父线程的MDC内容传给子线程。
**/
public class MdcThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
/**
* 所有线程都会委托给这个execute方法,在这个方法中我们把父线程的MDC内容赋值给子线程
* https://logback.qos.ch/manual/mdc.html#managedThreads
*
* @param runnable
*/
@Override
public void execute(Runnable runnable) {
// 获取父线程MDC中的内容,必须在run方法之前,否则等异步线程执行的时候有可能MDC里面的值已经被清空了,这个时候就会返回null
Map<String, String> context = MDC.getCopyOfContextMap();
super.execute(() -> run(runnable, context));
}
/**
* 子线程委托的执行方法
*
* @param runnable {@link Runnable}
* @param context 父线程MDC内容
*/
private void run(Runnable runnable, Map<String, String> context) {
// 将父线程的MDC内容传给子线程
MDC.setContextMap(context);
try {
// 执行异步操作
runnable.run();
} finally {
// 清空MDC内容
MDC.clear();
}
}
}
package com.qkdata.common.trace;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Slf4j
public class TraceIdInterceptor implements HandlerInterceptor {
public static final String TRACE_ID_KEY = "traceId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String traceId = UUID.randomUUID().toString().replaceAll("-", "");
MDC.put(TRACE_ID_KEY, traceId);
return true;
}
}
package com.qkdata.common.trace;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class TraceLog implements Serializable {
/**
* traceId
*/
private String traceId;
/**
* 请求路径
*/
private String path;
/**
* 请求方法
*/
private String method;
/**
* 请求头信息
*/
private String headers;
/**
* query请求参数
*/
private String parameters;
/**
* 接口花费时间
*/
private Long timeTaken;
/**
* 日志创建时间
*/
private LocalDateTime createAt;
/**
* Response Status
*/
private Integer status;
/**
* 请求Body
*/
private String requestBody;
/**
* 响应Body
*/
private String responseBody;
}
package com.qkdata.common.tree;
import java.util.List;
/**
* @author liu_yang
* 构造树结构时内容提供者接口
*
*/
public interface IContentProvider {
/**
* 获取当前元素下的子元素.
*
* @param curElement 当前元素对象
* @param level 当前元素级别
* @param index 当前元素所在层级索引
* @return 子元素对象集合
* @throws ServiceException 抛出异常
*/
List<? extends Object> getChildren(Object curElement, final int level, final int index);
/**
* 获取根元素.
*
* @param treeData 树结构所需数据
* @param level 当前级别
* @param index 当前索引
* @return 根元素对象集合
*/
List<? extends Object> getRootElement(List<? extends Object> treeData, final int level, final int index);
/**
* 异步时需实现此方法,用于判断当前元素是否有子元素.
*
* @param curElement 当前元素对象
* @param level 当前元素级别
* @param index 当前元素所在层级索引
* @return true/false
*/
boolean hasChild(Object curElement, final int level, final int index);
}
package com.qkdata.common.tree;
import java.util.Map;
/**
* @author liu_yang
* 构造树结构时节点显示内容提供者接口
*
*/
public interface ILabelProvider {
/**
* 获得当前元素类型Normal,Checkbox,Radio.
*
* @param curElement 当前元素对象
* @param level 级别
* @param index 索引
* @return TreeTypeEnum枚举
*/
TreeTypeEnum getLabelType(final Object curElement, final int level, final int index);
/**
* 获取当前元素显示名称.
*
* @param curElement 当前元素对象
* @param level 级别
* @param index 索引
* @return 节点名称
*/
String getLabelName(final Object curElement, final int level, final int index);
/**
* 获得当前元素的ID.
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return 节点ID
*/
String getLabelId(final Object curElement, final int level, final int index);
/**
* 获取当前元素显示图标.如果不显示图标,返回null
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return 节 点图标名称
*/
String getIcon(final Object curElement, final int level, final int index);
/**
* 获取当前元素是否显示默认图标.
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return true/false
*/
boolean isDefaultIcon(final Object curElement, final int level, final int index);
/**
* 获取当前元素是否可以点击操作
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return true/false
*/
boolean isClick(final Object curElement, int level, int index);
/**
* 如是单选或多选情况下会调用该方法,判断当前节点是否被选中.
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return true/false
*/
boolean isCheck(final Object curElement, int level, int index);
/**
* 如是单选或多选情况下会调用该方法,获取节点选中时设置的值.
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return 节点值
*/
String getCheckId(final Object curElement, int level, int index);
/**
* 获得当前元素的自定义属性.
*
* @param curElement 当前元素
* @param level 级别
* @param index 索引
* @return 自定义属性MAP
*/
Map<String, String> getAttributes(final Object curElement, int level, int index);
}
package com.qkdata.common.tree;
import java.util.ArrayList;
import java.util.List;
/**
* @author liu_yang
* 树对象
*
*/
public class Tree {
/**
* <code>childs</code> - 树子节点.
*/
private List<TreeNode> m_roots = new ArrayList<TreeNode>();
/**
* 树ID
*/
private String m_id = "";
/**
* Constructors.
*
* @param id 树ID
*/
public Tree(final String id) {
this.m_id = id;
}
/**
* Constructors.
*
* @param id 树ID
* @param roots 子节点TreeNode
*/
public Tree(final String id, final List<TreeNode> roots) {
this.m_roots = roots;
this.m_id = id;
}
/**
* 添加子节点
*
* @param treeNode TreeNode节点对象
*/
public void addChild(final TreeNode treeNode) {
this.m_roots.add(treeNode);
}
/**
* 获取List<TreeNode>树
*
* @return List<TreeNode>
*/
public List<TreeNode> getRoots() {
return this.m_roots;
}
/**
* @return id - {return content description}
*/
public String getId() {
return m_id;
}
/**
* @param id - {parameter description}.
*/
public void setId(final String id) {
m_id = id;
}
}
package com.qkdata.common.tree;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author liu_yang
* 树节点类
*
*/
public class TreeNode {
/**
* <code>id</code> - 节点ID.
*/
private String m_id;
/**
* <code>m_lable</code> - 节点名称.
*/
private String m_name;
/**
* <code>m_icon</code> - 图片.
*/
private String m_icon;
/**
* <code>m_children</code> - 子节点.
*/
private List<TreeNode> m_children;
/**
* <code>m_isExpand</code> - 是否可展开.
*/
private boolean m_isExpand;
/**
* <code>m_isClick</code> - 节点是否可点击.
*/
private boolean m_isClick;
/**
* 节点是否被选中
*/
private boolean m_isCheck;
/**
* <code>m_type</code> - 节点类型.
*/
private String m_type;
/**
* <code>extProperty</code> - {description}.
*/
private Map<String, String> m_extProperty;
/**
* 构造方法.
*
* @param id 节点ID.
* @param values 节点属性集合.
*/
public TreeNode(final String id, final Map<String, String> values) {
this.m_id = id;
this.m_extProperty = values;
}
/**
* @return id - {return content description}
*/
public String getId() {
return m_id;
}
/**
* @param id - {parameter description}.
*/
public void setId(final String id) {
m_id = id;
}
/**
* @return name - {return content description}
*/
public String getName() {
return m_name;
}
/**
* @param name - {parameter description}.
*/
public void setName(final String name) {
m_name = name;
}
/**
* @return icon - {return content description}
*/
public String getIcon() {
return m_icon;
}
/**
* @param icon - {parameter description}.
*/
public void setIcon(final String icon) {
m_icon = icon;
}
/**
* @return children - {return content description}
*/
public List<TreeNode> getChildren() {
return m_children;
}
/**
* @param children - {parameter description}.
*/
public void setChilds(final List<TreeNode> children) {
m_children = children;
}
public boolean getIsCheck() {
return m_isCheck;
}
public void setIsCheck(boolean isCheck) {
this.m_isCheck = isCheck;
}
/**
* @return isExpand - {return content description}
*/
public boolean getIsExpand() {
return m_isExpand;
}
/**
* @param isExpand - {parameter description}.
*/
public void setIsExpand(final boolean isExpand) {
m_isExpand = isExpand;
}
/**
* @return isClick - {return content description}
*/
public boolean getIsClick() {
return m_isClick;
}
/**
* @param isClick - {parameter description}.
*/
public void setIsClick(final boolean isClick) {
m_isClick = isClick;
}
/**
* @return type - {return content description}
*/
public String getType() {
return m_type;
}
/**
* @param type - {parameter description}.
*/
public void setType(final String type) {
m_type = type;
}
/**
* @return extProperty - {return content description}
*/
public Map<String, String> getExtProperty() {
return m_extProperty;
}
/**
* @param extProperty - {parameter description}.
*/
public void setExtProperty(final Map<String, String> extProperty) {
m_extProperty = extProperty;
}
/**
* addChild.
*
* @param node node
*/
public void addChild(final TreeNode node) {
if (this.m_children == null) {
this.m_children = new ArrayList<TreeNode>();
}
m_children.add(node);
}
}
package com.qkdata.common.tree;
/**
* @author liu_yang
* 树类型枚举
*
*/
public enum TreeTypeEnum {
/**
* 树节点可多选
*/
CHECKBOX("checkbox"),
/**
* 树节点可单选
*/
RADIO("radio"),
/**
* 树节点为普通节点
*/
NORMAL("normal");
/**
* 类型值
*/
private String value;
/**
* @param value
*/
private TreeTypeEnum(final String value) {
this.value = value;
}
/**
* @return
*/
public String value() {
return this.value;
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment