Commit 7409526c 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 fywlsoft.cn:57802/library/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
HEALTHCHECK --timeout=5s --start-period=60s \
CMD curl -f http://localhost/wx-monitor-server/actuator/health || exit 1
#!/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 tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@ComponentScan(basePackages = {
"com.qkdata"
})
@MapperScan("com.qkdata.**.repository")
@ServletComponentScan
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package com.qkdata.biz.base.constants;
public final class Constants {
public static final String PSAAWORD = "123456";
}
package com.qkdata.biz.base.constants;
import com.qkdata.common.base.enums.BaseResponseEnum;
public enum ResultEnum implements BaseResponseEnum {
ACCOUNT_OFF("14000", "账号已被禁止登录"),
TOKEN_TIME_OUT("14001", "登录失效,请重新登录"),
TOKEN_ERROR("14002", "token验证失败"),
CAPTCHA_ERROR("14004", "验证码错误"),
MOBILE_ERROR("14005", "手机号不存在"),
SMS_ERROR("14007", "短信发送失败"),
ACCOUNT_NOT_EXIST("14008","帐号不存在"),
PWD_ERROR("14009","密码错误");
private String value;
private String text;
ResultEnum(String value, String text) {
this.value = value;
this.text = text;
}
public String value() {
return value;
}
public String text() {
return text;
}
}
package com.qkdata.biz.base.enums;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.qkdata.common.base.enums.BasePOEnum;
public enum AccountStatusEnum implements BasePOEnum {
DISABLE(0, "禁用"), ENABLE(1, "启用");
private Integer value;
private String text;
AccountStatusEnum(Integer value, String text) {
this.value = value;
this.text = text;
}
@Override
public int value() {
return value;
}
@JsonCreator
public static AccountStatusEnum valueOf(Integer value) {
return BasePOEnum.valueOf(AccountStatusEnum.class, value);
}
}
package com.qkdata.biz.base.enums;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.qkdata.common.base.enums.BasePOEnum;
public enum MenuTypeEnum implements BasePOEnum {
FOLDER(0,"目录"),MENU(1,"菜单"),BUTTON(3,"按扭");
MenuTypeEnum(Integer value,String text){
this.value = value;
this.text = text;
}
private Integer value;
private String text;
@Override
public int value() {
return value;
}
@JsonCreator
public static MenuTypeEnum valueOf(Integer value) {
return BasePOEnum.valueOf(MenuTypeEnum.class, value);
}
}
package com.qkdata.biz.base.enums;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.qkdata.common.base.enums.BasePOEnum;
public enum SysConfigStatusEnum implements BasePOEnum {
HIDDEN(0, "隐藏"), SHOW(1, "显示");
private Integer value;
private String text;
SysConfigStatusEnum(Integer value, String text) {
this.value = value;
this.text = text;
}
@Override
public int value() {
return value;
}
@JsonCreator
public static SysConfigStatusEnum valueOf(Integer value) {
return BasePOEnum.valueOf(SysConfigStatusEnum.class, value);
}
}
package com.qkdata.biz.sys.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.qkdata.biz.sys.model.LoginModel;
import com.qkdata.biz.sys.model.LoginUserInfo;
import com.qkdata.biz.sys.service.ShiroService;
import io.swagger.annotations.Api;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/api/sys")
@Api
public class SysLoginController {
@Autowired
private ShiroService shiroService;
@PostMapping("/login")
public LoginUserInfo login(@RequestBody @Valid LoginModel loginModel) throws JsonProcessingException {
return shiroService.login(loginModel.getUsername(),loginModel.getPassword());
}
@GetMapping("/logout")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void logout(){
SecurityUtils.getSubject().logout();
}
}
package com.qkdata.biz.sys.controller;
import com.google.common.collect.Lists;
import com.qkdata.biz.sys.entity.SysUserPO;
import io.swagger.annotations.Api;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Api
@RestController
@RequestMapping("/api/sys/user")
public class UserController {
@GetMapping("/list")
// @RequiresPermissions("sys:user:list")
public List<SysUserPO> list(){
SysUserPO user = (SysUserPO) SecurityUtils.getSubject().getPrincipal();
return Lists.newArrayList();
}
}
package com.qkdata.biz.sys.entity;
import com.qkdata.biz.base.enums.SysConfigStatusEnum;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Table;
@Data
@Table(name = "sys_config")
public class SysConfigPO extends BasePO {
private String paramKey;
private String paramValue;
@Column(name = "status")
private SysConfigStatusEnum status;
private String remark;
}
package com.qkdata.biz.sys.entity;
import com.qkdata.biz.base.enums.MenuTypeEnum;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
import javax.persistence.Table;
@Data
@Table(name = "sys_menu")
public class SysMenuPO extends BasePO {
private Integer parentId;
private String name;
private String url;
private String perms;
private MenuTypeEnum type;
private String icon;
private Integer orderNum;
}
package com.qkdata.biz.sys.entity;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
import javax.persistence.Table;
@Data
@Table(name = "sys_oss")
public class SysOssPO extends BasePO {
private String url;
}
package com.qkdata.biz.sys.entity;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
import javax.persistence.Table;
@Data
@Table(name = "sys_role_menu")
public class SysRoleMenuPO extends BasePO {
private Integer roleId;
private Integer menuId;
}
package com.qkdata.biz.sys.entity;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
import javax.persistence.Table;
@Data
@Table(name = "sys_role")
public class SysRolePO extends BasePO {
private String name;
private String remark;
}
package com.qkdata.biz.sys.entity;
import com.qkdata.biz.base.enums.AccountStatusEnum;
import com.qkdata.common.base.entity.BasePO;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Table;
import java.time.LocalDateTime;
@Data
@Table(name = "sys_user")
public class SysUserPO extends BasePO {
private String username;
private String password;
private String salt;
private String email;
private String mobile;
@Column(name = "status")
private AccountStatusEnum status;
/**
* 创建时间
*/
@Column(name = "create_at")
private LocalDateTime createAt;
}
package com.qkdata.biz.sys.entity;
import lombok.Data;
import javax.persistence.Table;
@Data
@Table(name = "sys_user_role")
public class SysUserRolePO {
private Long userId;
private Long roleId;
}
package com.qkdata.biz.sys.model;
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.model;
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.repository;
import com.qkdata.biz.sys.entity.SysConfigPO;
import tk.mybatis.mapper.common.Mapper;
public interface SysConfigMapper extends Mapper<SysConfigPO> {
}
package com.qkdata.biz.sys.repository;
import com.qkdata.biz.sys.entity.SysMenuPO;
import tk.mybatis.mapper.common.Mapper;
public interface SysMenuMapper extends Mapper<SysMenuPO> {
}
package com.qkdata.biz.sys.repository;
import com.qkdata.biz.sys.entity.SysOssPO;
import tk.mybatis.mapper.common.Mapper;
public interface SysOssMapper extends Mapper<SysOssPO> {
}
package com.qkdata.biz.sys.repository;
import com.qkdata.biz.sys.entity.SysRolePO;
import tk.mybatis.mapper.common.Mapper;
public interface SysRoleMapper extends Mapper<SysRolePO> {
}
package com.qkdata.biz.sys.repository;
import com.qkdata.biz.sys.entity.SysRoleMenuPO;
import tk.mybatis.mapper.common.Mapper;
public interface SysRoleMenuMapper extends Mapper<SysRoleMenuPO> {
}
package com.qkdata.biz.sys.repository;
import com.qkdata.biz.sys.entity.SysUserPO;
import tk.mybatis.mapper.common.Mapper;
import java.util.List;
public interface SysUserMapper extends Mapper<SysUserPO> {
List<String> queryAllPerms(Long userId);
}
package com.qkdata.biz.sys.repository;
import com.qkdata.biz.sys.entity.SysUserRolePO;
import tk.mybatis.mapper.common.Mapper;
public interface SysUserRoleMapper extends Mapper<SysUserRolePO> {
}
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.base.constants.ResultEnum;
import com.qkdata.biz.base.enums.AccountStatusEnum;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.jwt.JWTService;
import com.qkdata.common.oauth.AuthorizedUser;
import com.qkdata.biz.sys.entity.SysMenuPO;
import com.qkdata.biz.sys.entity.SysUserPO;
import com.qkdata.biz.sys.model.LoginUserInfo;
import com.qkdata.biz.sys.repository.SysMenuMapper;
import com.qkdata.biz.sys.repository.SysUserMapper;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.*;
@Service
@Transactional(readOnly = true)
public class ShiroService {
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private SysMenuMapper sysMenuMapper;
@Autowired
private JWTService jwtService;
private ObjectMapper mapper = new ObjectMapper();
@Cacheable(cacheNames = "userPerm_userId",unless = "#result == null")
public Set<String> getUserPermissions(Long userId) {
List<String> permsList;
//系统管理员,拥有最高权限
if(userId == 1){
List<SysMenuPO> menuList = sysMenuMapper.selectAll();
permsList = new ArrayList<>(menuList.size());
for(SysMenuPO menu : menuList){
permsList.add(menu.getPerms());
}
}else{
permsList = sysUserMapper.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;
}
@Cacheable(cacheNames = "user_username",unless = "#result == null")
public SysUserPO getUserByUserName(String username) {
SysUserPO query = new SysUserPO();
query.setUsername(username);
return sysUserMapper.selectOne(query);
}
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(ResultEnum.ACCOUNT_NOT_EXIST);
}
if (userPO.getStatus() == AccountStatusEnum.DISABLE){
throw new BusinessException(ResultEnum.ACCOUNT_OFF);
}
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 = mapper.writeValueAsString(user);
Map<String,Object> userClaim = Maps.newConcurrentMap();
userClaim.put("user",userJson);
return jwtService.createJWT(userClaim);
}
}
package com.qkdata.biz.wsMessage.api;
import com.qkdata.biz.wsMessage.service.WSMessageService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
@Api(description = "测试websocket")
@RestController
@RequestMapping("/api/v1/testWebsocket")
public class WSMessageController {
@Autowired
private WSMessageService wsMessageService;
@ApiOperation("给指定用户发消息")
@GetMapping("/sendToUser")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void sendMessageToUser(@RequestParam String user, @RequestParam String message){
// wsMessageService.sendRoomChatMsgToUser(user,message);
}
}
package com.qkdata.biz.wsMessage.config;
import java.security.Principal;
public class MyPrincipal implements Principal {
private String name;
public MyPrincipal(String name){
this.name = name;
}
@Override
public String getName() {
return this.name;
}
}
package com.qkdata.biz.wsMessage.config;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qkdata.common.oauth.AuthorizationException;
import com.qkdata.common.oauth.AuthorizationResponseEnum;
import com.qkdata.common.jwt.JWTService;
import com.qkdata.common.oauth.AuthorizedUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import java.io.IOException;
import java.security.Principal;
import java.util.Map;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Autowired
@Lazy
private JWTService jwtService;
@Autowired
@Lazy
private ObjectMapper objectMapper;
//注册STOMP协议节点并映射url
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket") //注册一个 /websocket 的 websocket 节点
.addInterceptors(myHandshakeInterceptor()) //添加 websocket握手拦截器
.setHandshakeHandler(myDefaultHandshakeHandler()) //添加 websocket握手处理器
.setAllowedOrigins("*") //设置允许可跨域的域名
.withSockJS(); //指定使用SockJS协议
}
/**
* WebSocket 握手拦截器
* 可做一些用户认证拦截处理
*/
private HandshakeInterceptor myHandshakeInterceptor(){
return new HandshakeInterceptor() {
/**
* websocket握手连接
* @return 返回是否同意握手
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
ServletServerHttpRequest req = (ServletServerHttpRequest) request;
//通过url的query参数获取认证参数
String token = req.getServletRequest().getParameter("token");
//根据token认证用户,不通过返回拒绝握手
if (token == null){
return false;
}
Principal user = authenticate(token);
if(user == null){
return false;
}
//保存认证用户
attributes.put("user", user);
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
}
};
}
//WebSocket 握手处理器
private DefaultHandshakeHandler myDefaultHandshakeHandler(){
return new DefaultHandshakeHandler(){
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
//设置认证通过的用户到当前会话中
return (Principal)attributes.get("user");
}
};
}
/**
* 定义一些消息连接规范(也可不设置)
* @param registry
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//设置客户端接收消息地址的前缀(可不设置)
registry.enableSimpleBroker(
"/topic/room","/topic/login"
);
//设置客户端接收点对点消息地址的前缀,默认为 /user
registry.setUserDestinationPrefix("/user");
//设置客户端向服务器发送消息的地址前缀(可不设置)
registry.setApplicationDestinationPrefixes("/app");
}
/**
* 根据token认证授权
* @param token
*/
private Principal authenticate(String token){
//用户信息需继承 Principal 并实现 getName() 方法,返回全局唯一值
DecodedJWT jwtToken = jwtService.decode(token);
String userJson = jwtToken.getClaim("user").asString();
if (StringUtils.isEmpty(userJson)) {
return null;
}
try {
AuthorizedUser authorizedUser = objectMapper.readValue(userJson, AuthorizedUser.class);
return new MyPrincipal(authorizedUser.getUsername());
} catch (IOException e) {
throw new AuthorizationException(AuthorizationResponseEnum.INVALID_CLAIM);
}
}
}
package com.qkdata.biz.wsMessage.model;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class WxLoginMsg {
private String type;//qrcode|login_result
private String content;
}
package com.qkdata.biz.wsMessage.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class WSMessageService {
@Autowired
private SimpMessagingTemplate template;
// public void sendRoomChatMsgToUser(String user,String message){
// log.debug("WSMessageService sendRoomChatMsgToUser user:"+user);
// template.convertAndSendToUser(user,"/topic/room",message);
// }
// public void sendLoginMsgToUser(String user, WxLoginMsg msg){
// log.debug("WSMessageService sendLoginMsgToUser,user={}",user);
// template.convertAndSendToUser(user,"/topic/login", JSONObject.toJSONString(msg));
// }
}
package com.qkdata.common.base.entity;
import com.qkdata.common.base.enums.DeletedEnum;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class BasePO implements Serializable {
private static final long serialVersionUID = -5785859031130509619L;
/**
* 主键
*/
@Id
@Column(name = "id")
@GeneratedValue(generator = "JDBC")
protected Long id;
}
package com.qkdata.common.base.enums;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.HashMap;
import java.util.Map;
/**
* 所有Entity中枚举类型的基础, 主要用于数据库中存储枚举的value和SpringMVC参数直接转换成Enum
*
* @author chenjiahai
* @date 17/8/3
*/
public interface BasePOEnum {
/**
* JsonValue用于serializer
*
* @return
*/
@JsonValue
int value();
/**
* 获取枚举值对应的枚举
*
* @param enumClass 枚举类
* @param enumValue 枚举值
* @return 枚举
*/
static <E extends BasePOEnum> E getEnum(final Class<E> enumClass, final Integer enumValue) {
if (enumValue == null) {
return null;
}
try {
return valueOf(enumClass, enumValue);
} catch (final IllegalArgumentException ex) {
return null;
}
}
/**
* 获取枚举值对应的枚举
*
* @param enumClass 枚举类
* @param enumValue 枚举值
* @return 枚举
*/
static <E extends BasePOEnum> E valueOf(Class<E> enumClass, Integer enumValue) {
if (enumValue == null) {
return null;
}
return getEnumMap(enumClass).get(enumValue);
}
/**
* 获取枚举键值对
*
* @param enumClass 枚举类型
* @return 键值对
*/
static <E extends BasePOEnum> Map<Integer, E> getEnumMap(Class<E> enumClass) {
E[] enums = enumClass.getEnumConstants();
if (enums == null) {
throw new IllegalArgumentException(enumClass.getSimpleName() + " does not represent an enum type.");
}
Map<Integer, E> map = new HashMap<>();
for (E t : enums) {
map.put(t.value(), t);
}
return map;
}
}
package com.qkdata.common.base.enums;
public interface BaseResponseEnum {
String value();
String text();
}
package com.qkdata.common.base.enums;
import com.fasterxml.jackson.annotation.JsonCreator;
/**
* 删除标志枚举
*/
public enum DeletedEnum implements BasePOEnum {
DELETED(1, "已删除"),
NOT_DELETED(2, "未删除");
private Integer value;
private String text;
DeletedEnum(Integer value, String text) {
this.value = value;
this.text = text;
}
@Override
public int value() {
return value;
}
@JsonCreator
public static DeletedEnum valueOf(Integer value) {
return BasePOEnum.valueOf(DeletedEnum.class, value);
}
}
package com.qkdata.common.base.enums;
public enum SystemResponseEnum implements BaseResponseEnum {
ERROR("500", "服务器繁忙, 请稍后再试"),
INVALID_TIMESTAMP("400", "不合法的时间格式"),
MESSAGE_NOT_READABLE("400", "无法解析请求参数"),
MISSING_PATH_VARIABLE("400", "请求链接缺少参数"),
MISSING_REQUEST_PARAM("400", "缺少必需的请求参数"),
ARGUMENT_TYPE_MISMATCH("400", "参数类别转换失败"),
CONSTRAINT_VIOLATION("400", "参数校验失败"),
METHOD_NOT_SUPPORTED("400", "不支持该请求方法"),
MEDIA_TYPE_NOT_SUPPORTED("400", "不支持该请求参数类型"),
NO_HANDLER_NOT_FOUND("404", "请求资源不存在");
private String value;
private String text;
SystemResponseEnum(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.base.exception;
import com.qkdata.common.base.enums.BaseResponseEnum;
import lombok.Data;
/**
* 业务异常
*/
@Data
public class BusinessException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 4650570427238258122L;
private String code;
private String message;
public BusinessException(BaseResponseEnum responseEnum) {
this.code = responseEnum.value();
this.message = responseEnum.text();
}
public BusinessException(String code, String message) {
this.code = code;
this.message = message;
}
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.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author songminghui
* @date 2018-11-15 上午11:11
* @email songminghui@shangweiec.com
* @description
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IdDTO implements Serializable {
private static final long serialVersionUID = -1524848284951435773L;
private Long id;
}
package com.qkdata.common.base.model;
import com.github.pagehelper.PageInfo;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
public class PageDTO<T> implements Serializable {
/**
*
*/
private static final long serialVersionUID = -3506803903226539566L;
/**
* 总条数
*/
private Long total = 0L;
/**
* 数据
*/
private List<?> result = new ArrayList<>();
public PageDTO(PageInfo<T> pageInfo) {
this.total = pageInfo.getTotal();
List<T> list = pageInfo.getList();
if (!CollectionUtils.isEmpty(list))
this.result = pageInfo.getList();
}
public PageDTO(PageInfo<T> pageInfo, List<T> result) {
this.total = pageInfo.getTotal();
if (!CollectionUtils.isEmpty(result))
this.result = result;
}
}
package com.qkdata.common.base.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.qkdata.common.base.enums.BaseResponseEnum;
import lombok.Data;
import java.io.Serializable;
@SuppressWarnings("deprecation")
@JsonInclude(content = JsonInclude.Include.NON_NULL, value = JsonInclude.Include.NON_EMPTY)
@Data
public class ResponseData implements Serializable {
private static final long serialVersionUID = 645306359281445009L;
private String code;
private String message;
private Object data;
public ResponseData() {
}
public ResponseData(BaseResponseEnum responseEnum) {
this.code = responseEnum.value();
this.message = responseEnum.text();
}
public ResponseData(Object data) {
this.data = data;
}
public ResponseData(String code, String message) {
this.code = code;
this.message = message;
}
}
package com.qkdata.common.base.service;
import java.util.List;
/**
* service基础类
*/
public interface BaseService<T, Serializable> {
/**
* 根据主键ID查询
*
* @param id
* @return
*/
T getById(Serializable id);
/**
* 保存
*
* @param entity
*/
void save(T entity);
/**
* 根据主键ID更新
*
* @param entity
*/
void updateById(T entity);
/**
* 根据指定条件查询列表
*
* @param entity
* @return
*/
List<T> listByCondition(T entity);
/**
* 根据指定条件查询单个实体
*
* @param entity
* @return
*/
T getByCondition(T entity);
/**
* 根据指定条件查询数量
* @param entity
* @return
*/
int countByCondition(T entity);
void deleteById(Serializable id);
}
package com.qkdata.common.base.service.impl;
import com.qkdata.common.base.service.BaseService;
import tk.mybatis.mapper.common.Mapper;
import java.util.List;
public abstract class BaseServiceImpl<T, Serializable> implements BaseService<T, Serializable> {
protected abstract Mapper<T> getMapper();
@Override
public T getById(Serializable id) {
return getMapper().selectByPrimaryKey(id);
}
@Override
public void save(T entity) {
getMapper().insertSelective(entity);
}
@Override
public void updateById(T entity) {
getMapper().updateByPrimaryKeySelective(entity);
}
@Override
public List<T> listByCondition(T entity) {
return getMapper().select(entity);
}
@Override
public T getByCondition(T entity) {
return getMapper().selectOne(entity);
}
@Override
public int countByCondition(T entity) {
return getMapper().selectCount(entity);
}
@Override
public void deleteById(Serializable id) {
getMapper().deleteByPrimaryKey(id);
}
}
package com.qkdata.common.config;
import com.qkdata.biz.base.enums.AccountStatusEnum;
import com.qkdata.common.base.enums.BasePOEnum;
import com.qkdata.common.base.enums.DeletedEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@MappedTypes(value = {
DeletedEnum.class,
AccountStatusEnum.class})
public class CustomEnumTypeHandler<E extends BasePOEnum> extends BaseTypeHandler<E> {
// org.apache.ibatis.type.TypeHandlerRegistry#register(TypeHandler<T> typeHandler)#Line:292
private Class<E> type;
public CustomEnumTypeHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.value());
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
int i = rs.getInt(columnName);
if (rs.wasNull()) {
return null;
} else {
try {
return BasePOEnum.getEnum(type, i);
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by int value.", ex);
}
}
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int i = rs.getInt(columnIndex);
if (rs.wasNull()) {
return null;
} else {
try {
return BasePOEnum.getEnum(type, i);
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by int value.", ex);
}
}
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int i = cs.getInt(columnIndex);
if (cs.wasNull()) {
return null;
} else {
try {
return BasePOEnum.getEnum(type, i);
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by int value.", ex);
}
}
}
}
\ 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.oauth.AuthorizationException;
import com.qkdata.common.base.enums.SystemResponseEnum;
import com.qkdata.common.base.exception.BusinessException;
import com.qkdata.common.base.model.ResponseData;
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.ConstraintViolation;
import javax.validation.ConstraintViolationException;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandle {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseEntity<ResponseData> bindExceptionHandler(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
String message = bindingResult.getFieldError().getDefaultMessage();
log.warn("方法参数无效: {}", message);
return ResponseEntity.badRequest().body(new ResponseData("70000", message));
}
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public ResponseEntity<ResponseData> httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e) {
log.warn("不支持的Method: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ResponseData(SystemResponseEnum.METHOD_NOT_SUPPORTED));
}
@ExceptionHandler(value = Exception.class)
public ResponseEntity<ResponseData> handler(Exception e) {
log.error("服务器错误", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ResponseData(SystemResponseEnum.ERROR));
}
@ExceptionHandler(value = BusinessException.class)
public ResponseEntity<ResponseData> businessExceptionhandler(BusinessException e) {
log.warn("业务异常, code: {}, message: {}", e.getCode(), e.getMessage());
// TODO 从BusinessException获取responseEnum, 不能直接打印e.getMessage()
return ResponseEntity.badRequest().body(new ResponseData(e.getCode(), e.getMessage()));
}
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public ResponseEntity<ResponseData> httpMessageConvertExceptionHanlder(HttpMessageNotReadableException e) {
log.warn("无法解析请求参数: {}", e.getMessage());
return ResponseEntity.badRequest().body(new ResponseData(SystemResponseEnum.MESSAGE_NOT_READABLE));
}
@ExceptionHandler(value = MissingPathVariableException.class)
public ResponseEntity<ResponseData> missingPathVariableExceptionHanlder(MissingPathVariableException e) {
log.warn("请求链接缺少参数: {}", e.getMessage());
return ResponseEntity.badRequest().body(new ResponseData(SystemResponseEnum.MISSING_PATH_VARIABLE));
}
@ExceptionHandler(value = MissingServletRequestParameterException.class)
public ResponseEntity<ResponseData> requestParameterExceptionHandler(MissingServletRequestParameterException e) {
log.warn("缺少必需的请求参数: {}", e.getMessage());
return ResponseEntity.badRequest().body(new ResponseData(SystemResponseEnum.MISSING_REQUEST_PARAM));
}
@ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
public ResponseEntity<ResponseData> methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException e) {
log.warn("参数类别转换失败: {}", e.getMessage());
return ResponseEntity.badRequest().body(new ResponseData(SystemResponseEnum.ARGUMENT_TYPE_MISMATCH));
}
@ExceptionHandler(value = ConstraintViolationException.class)
public ResponseEntity<ResponseData> constraintViolationExceptionHandler(ConstraintViolationException e) {
log.warn("参数校验异常: {}", e.getMessage());
return ResponseEntity.badRequest().body(new ResponseData(SystemResponseEnum.CONSTRAINT_VIOLATION.value(), e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).findFirst().get()));
}
@ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
public ResponseEntity<ResponseData> httpMediaTypeNotSupportedExceptionHandler(HttpMediaTypeNotSupportedException e) {
log.warn("不支持MediaType: {}", e.getMessage());
return ResponseEntity.badRequest().body(new ResponseData(SystemResponseEnum.MEDIA_TYPE_NOT_SUPPORTED));
}
@ExceptionHandler(value = NoHandlerFoundException.class)
public ResponseEntity<ResponseData> noHandlerFoundExceptionHandler(NoHandlerFoundException e) {
log.warn("no handler found: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ResponseData(SystemResponseEnum.NO_HANDLER_NOT_FOUND));
}
@ExceptionHandler(value = AuthorizationException.class)
public ResponseEntity<ResponseData> authorizationExceptionHandler(AuthorizationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ResponseData(e.responseEnum()));
}
}
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.LocalDateDeserializer;
import com.qkdata.common.converter.LocalDateSerializer;
import com.qkdata.common.converter.LocalDateTimeDeserializer;
import com.qkdata.common.converter.LocalDateTimeSerializer;
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;
@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(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.fasterxml.jackson.annotation.JsonAutoDetect;
//import com.fasterxml.jackson.annotation.PropertyAccessor;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.boot.autoconfigure.AutoConfigureAfter;
//import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
//import org.springframework.cache.annotation.EnableCaching;
//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;
//
//@Configuration
//@EnableCaching
//@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);
// 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(5000);//单位为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.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
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().globalOperationParameters(pars);
}
/**
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("wx monitor service API").version("1.0").build();
}
}
package com.qkdata.common.config;
import com.qkdata.common.converter.StringToCommonEnumConverterFactory;
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.core.convert.converter.ConverterFactory;
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("/api/**")
.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.addConverterFactory(stringToEnumConverterFactory());
registry.addConverter(stringToLocalDateConverter());
registry.addConverter(stringToLocalDateTimeConverter());
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(traceIdInterceptor());
}
private HandlerInterceptor traceIdInterceptor() {
return new TraceIdInterceptor();
}
private ConverterFactory stringToEnumConverterFactory() {
return new StringToCommonEnumConverterFactory();
}
private Converter stringToLocalDateConverter() {
return new StringToLocalDateConverter();
}
private Converter stringToLocalDateTimeConverter() {
return new StringToLocalDateTimeConverter();
}
}
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 final class ValidatorPattern {
private ValidatorPattern(){}
public static final String MOBILE_PATTERN = "^(13|14|15|17|18|19)[0-9]{9}$";
}
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.enums.SystemResponseEnum;
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(SystemResponseEnum.INVALID_TIMESTAMP);
}
}
}
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.enums.SystemResponseEnum;
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(SystemResponseEnum.INVALID_TIMESTAMP);
}
}
}
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 com.qkdata.common.base.enums.BasePOEnum;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.util.StringUtils;
/**
* 用于QUERY参数转换成对应枚举类型
*/
public class StringToCommonEnumConverterFactory implements ConverterFactory<String, BasePOEnum> {
@Override
public <T extends BasePOEnum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnum<>(targetType);
}
private static class StringToEnum<T extends BasePOEnum> implements Converter<String, T> {
private final Class<T> enumType;
public StringToEnum(Class<T> enumType) {
this.enumType = enumType;
}
@Override
public T convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
return BasePOEnum.getEnum(enumType, Integer.valueOf(source.trim()));
}
}
}
package com.qkdata.common.converter;
import com.qkdata.common.base.enums.SystemResponseEnum;
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(SystemResponseEnum.INVALID_TIMESTAMP);
}
}
}
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;
import com.qkdata.common.base.enums.BaseResponseEnum;
public class AuthorizationException extends RuntimeException {
private String message;
private BaseResponseEnum responseEnum;
public AuthorizationException(BaseResponseEnum responseEnum) {
this.responseEnum = responseEnum;
}
public AuthorizationException(String message, BaseResponseEnum responseEnum) {
this.message = message;
this.responseEnum = responseEnum;
}
public BaseResponseEnum responseEnum() {
return responseEnum;
}
}
package com.qkdata.common.oauth;
import com.qkdata.common.base.enums.BaseResponseEnum;
public enum AuthorizationResponseEnum implements BaseResponseEnum {
MISSING_TOKEN("401", "HEADER中不存在TOKEN"),
INVALID_TOKEN("401", "校验TOKEN失败"),
EXPIRED_TOKEN("401", "TOKEN已过期"),
MISSING_CLAIMS("401", "不合法的TOKEN, 信息可能被篡改"),
INVALID_CLAIM("401", "不合法的TOKEN, 系统中不存在资源");
private String value;
private String text;
AuthorizationResponseEnum(String value, String text) {
this.value = value;
this.text = text;
}
@Override
public String value() {
return this.value;
}
@Override
public String text() {
return this.text;
}
}
package com.qkdata.common.oauth;
import lombok.Data;
@Data
public class AuthorizedUser {
private Long userId;
private String username;
}
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
*
* 版权所有,侵权必究!
*/
package com.qkdata.common.oauth;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qkdata.common.base.model.ResponseData;
import com.qkdata.common.util.HttpContextUtils;
import org.apache.http.HttpStatus;
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不存在,直接返回401
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());
ResponseData responseData = new ResponseData(AuthorizationResponseEnum.MISSING_TOKEN);
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();
ResponseData rd = new ResponseData("401",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.base.enums.AccountStatusEnum;
import com.qkdata.biz.sys.entity.SysUserPO;
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.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 = shiroService.getUserPermissions(userId);
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 AuthorizationException("request header中没有token", AuthorizationResponseEnum.MISSING_TOKEN);
}
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;
}
}
package com.qkdata.common.tree;
import java.util.ArrayList;
import java.util.List;
/**
* @author liu_yang 生成树方法
*
*/
public class TreeView {
/**
* <code>m_treeData</code> - 树内容原始对象.
*/
private List<? extends Object> m_treeData;
/**
* <code>m_contentProvider</code> - 内容提供者.
*/
private IContentProvider m_contentProvider;
/**
* <code>m_labelProvider</code> - 节点内容提供者.
*/
private ILabelProvider m_labelProvider;
/**
* 生成树对象的构造方法
* @param treeData 生成树结构的数据
* @param contentProvider 树节点内容提供者
* @param labelProvider 树节点label显示内容提供者
*/
public TreeView(List<? extends Object> treeData,IContentProvider contentProvider,ILabelProvider labelProvider){
this.m_treeData=treeData;
this.m_contentProvider=contentProvider;
this.m_labelProvider=labelProvider;
}
/**
* 构造树.
*
* @param treeId
* Tree ID
* @return Tree对象
* @throws ServiceException
* Service异常
*/
public Tree buildTree(final String treeId) {
List<TreeNode> t_rootList = new ArrayList<TreeNode>();
TreeNode t_treeNode = null;
List<? extends Object> t_rootObjectList = m_contentProvider.getRootElement(this.m_treeData, 0, 0);
if (t_rootObjectList != null) {
for (int t_i = 0; t_i < t_rootObjectList.size(); t_i++) {
Object t_rootObject = t_rootObjectList.get(t_i);
t_treeNode = createTreeNode(t_rootObject, 0, t_i);
recursionNodes(this.m_contentProvider.getChildren(t_rootObject, 0, t_i), t_treeNode, 0);
t_rootList.add(t_treeNode);
}
}
Tree t_tree = new Tree(treeId, t_rootList);
return t_tree;
}
/**
* 递归树节点.
*
* @param objectList
* 树原始对象集合
* @param parentTreeNode
* 父树节点
* @param level
* 树节点集合
* @throws ServiceException
* 异常
*/
private void recursionNodes(final List<? extends Object> objectList, final TreeNode parentTreeNode,
final int level) {
int t_level = level + 1;
TreeNode t_treeNode = null;
if (objectList != null) {
for (int t_i = 0; t_i < objectList.size(); t_i++) {
Object t_childObject = objectList.get(t_i);
t_treeNode = createTreeNode(t_childObject, t_level, t_i);
parentTreeNode.addChild(t_treeNode);
recursionNodes(this.m_contentProvider.getChildren(t_childObject, t_level, t_i), t_treeNode, t_level);
}
}
}
/**
* 创建树节点.
*
* @param o
* 节点原始对象
* @param level
* 节点级别
* @param index
* 节点同级索引
* @return 创建的树节点
*/
private TreeNode createTreeNode(final Object o, final int level, final int index) {
TreeNode t_treeNode = new TreeNode(this.m_labelProvider.getLabelId(o, level, index),
this.m_labelProvider.getAttributes(o, level, index));
if (this.m_contentProvider.hasChild(o, level, index)) {
t_treeNode.setIsExpand(true);
} else {
t_treeNode.setIsExpand(false);
}
t_treeNode.setName(this.m_labelProvider.getLabelName(o, level, index));
t_treeNode.setIcon(this.m_labelProvider.getIcon(o, level, index));
t_treeNode.setIsClick(this.m_labelProvider.isClick(o, level, index));
t_treeNode.setIsCheck(this.m_labelProvider.isCheck(o, level, index));
createTreeNodeType(o, level, index, t_treeNode);
return t_treeNode;
}
/**
* 创建树节点类型.
*
* @param o
* Object
* @param level
* 级别
* @param index
* 索引
* @param treeNode
* 树节点
*/
private void createTreeNodeType(final Object o, final int level, final int index, final TreeNode treeNode) {
TreeTypeEnum t_treeType = this.m_labelProvider.getLabelType(o, level, index);
treeNode.setType(t_treeType.toString());
}
}
package com.qkdata.common.util;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.Validate;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class EncryptUtil {
private static final String SHA1 = "SHA-1";
private static final String MD5 = "MD5";
private static SecureRandom random = new SecureRandom();
public static String getSalt() {
byte[] saltByte = generateSalt(8);
String salt = encodeHex(saltByte);
return salt;
}
public static String encryptPassword(String password, String salt) throws NoSuchAlgorithmException {
byte[] hashPassword = md5(password.getBytes(), salt.getBytes(), 1024);
return encodeHex(hashPassword);
}
/**
* Hex编码.
*/
public static String encodeHex(byte[] input) {
return Hex.encodeHexString(input);
}
/**
* Hex解码.
* @throws DecoderException
*/
public static byte[] decodeHex(String input) throws DecoderException {
return Hex.decodeHex(input.toCharArray());
}
public static byte[] sha1(byte[] input, byte[] salt, int iterations) throws NoSuchAlgorithmException {
return digest(input, SHA1, salt, iterations);
}
public static byte[] md5(byte[] input, byte[] salt, int iterations) throws NoSuchAlgorithmException {
return digest(input, MD5, salt, iterations);
}
/**
* 对字符串进行散列, 支持md5与sha1算法.
* @throws NoSuchAlgorithmException
*/
private static byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(algorithm);
if (salt != null) {
digest.update(salt);
}
byte[] result = digest.digest(input);
for (int i = 1; i < iterations; i++) {
digest.reset();
result = digest.digest(result);
}
return result;
}
/**
* 生成随机的Byte[]作为salt.
*
* @param numBytes byte数组的大小
*/
private static byte[] generateSalt(int numBytes) {
Validate.isTrue(numBytes > 0, "numBytes argument must be a positive integer (1 or larger)", numBytes);
byte[] bytes = new byte[numBytes];
random.nextBytes(bytes);
return bytes;
}
}
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
*
* 版权所有,侵权必究!
*/
package com.qkdata.common.util;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class HttpContextUtils {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getDomain(){
HttpServletRequest request = getHttpServletRequest();
StringBuffer url = request.getRequestURL();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
}
public static String getOrigin(){
HttpServletRequest request = getHttpServletRequest();
return request.getHeader("Origin");
}
}
package com.qkdata.common.util;
import java.util.Objects;
import java.util.function.BiConsumer;
/**
* 带索引的forEach方法
*/
public class Iterables {
public static <E> void forEach(Iterable<? extends E> elements, BiConsumer<Integer, ? super E> action) {
Objects.requireNonNull(elements);
Objects.requireNonNull(action);
int index = 0;
for (E element : elements) {
action.accept(index++, element);
}
}
}
package com.qkdata.common.util;
import java.security.MessageDigest;
/**
* @author songminghui
* @date 2018-12-17 下午6:00
* @email songminghui@shangweiec.com
* @description
*/
public class MD5Util {
public static String md5(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(str.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
package com.qkdata.common.util;
import java.util.concurrent.ThreadLocalRandom;
/**
* 随机数生成器
*/
public final class RandomDigitGenerator {
private RandomDigitGenerator() {
}
private static final int DEFAULT_COUNT = 6;
private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
private static final int ORIGIN = 0;
private static final int BOUND = 10;
/**
* 默认生成6位数字
*
* @return
*/
public static String generate() {
return generate(DEFAULT_COUNT);
}
/**
* 生成指定长度的数字
*
* @param count 长度
* @return
*/
public static String generate(int count) {
if (count <= 0 || count > 1000) {
return "";
}
return RANDOM.ints(ORIGIN, BOUND)
.limit(count)
.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
.toString();
}
}
package com.qkdata.common.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Map;
/**
* @author songminghui
* @date 2018-11-26 下午6:52
* @email songminghui@shangweiec.com
* @description
*/
public class RequestUtil {
private RequestUtil() {
}
public static HttpServletRequest getRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getIpAddress() {
HttpServletRequest request = RequestUtil.getRequest();
String ip = request.getHeader("X-Real-IP");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("x-forwarded-for");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
public static String getParam(HttpServletRequest request) {
BufferedReader br = null;
try {
String method = request.getMethod();
if (HttpMethod.GET.matches(method)) {
Map<String, String[]> parameterMap = request.getParameterMap();
return new ObjectMapper().writeValueAsString(parameterMap);
}
br = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
} catch (Exception e) {
}
String line = null;
StringBuilder sb = new StringBuilder();
try {
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (Exception e) {
}
return sb.toString();
}
}
server:
port: 8080
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/framework?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
username: root
password: 123456
# redis:
# host: localhost
log:
path: ../data/logs
file:
rootPath: ../data/files
server:
port: 80
servlet:
context-path: /backend-java
management:
endpoint:
health:
show-details: always
spring:
datasource:
druid:
url: jdbc:mysql://mysql:3306/wx_monitor?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
initialSize: 5
minIdle: 5
maxActive: 30
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: false
maxPoolPreparedStatementPerConnectionSize: -1
filters: stat,slf4j,config
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMil=3000
useGlobalDataSourceStat: true
filter:
wall:
config:
multi-statement-allow: true
# redis:
# host: redis
# port: 6379
# timeout: 2s
# jedis:
# pool:
# max-active: 20
# max-wait: -1ms
# max-idle: 20
# min-idle: 0
# database: 0
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: true
main:
allow-bean-definition-overriding: true
# flyway:
# baseline-on-migrate: true
# placeholder-replacement: false
mybatis:
mapper-locations: classpath*:mappers/*.xml
type-handlers-package: com.qkdata.common.config
configuration:
map-underscore-to-camel-case: true
log:
context: backend-java
path: /data/logs
jwt:
secret: backend-java-prod
enable-interceptor: true
include-urls:
- /api/**
exclude-urls:
- /api/sys/login
aud: backend-java
exp: 24
file:
rootPath: /data/files
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProperty scope="context" name="LOG_PATH" source="log.path"/>
<springProperty scope="context" name="LOG_CONTEXT" source="log.context"/>
<property name="PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%X{traceId}] %-5level %logger{50} - %line %msg %n"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_PATH}/${LOG_CONTEXT}.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${LOG_CONTEXT}.log.%i.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>30</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>30MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<Pattern>${PATTERN}</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="sql" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_PATH}/${LOG_CONTEXT}-sql.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${LOG_CONTEXT}-sql.log.%i.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>30</maxHistory>
<TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>30MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<Pattern>${PATTERN}</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<logger name="com.qkdata" level="DEBUG" />
<!--<logger name="com.qkdata.biz.brandKeyword.repository" level="DEBUG" additivity="false">-->
<!--<appender-ref ref="sql" />-->
<!--</logger>-->
<root level="INFO">
<appender-ref ref="console" />
<appender-ref ref="rollingFile" />
</root>
</configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qkdata.biz.sys.repository.SysUserMapper">
<!-- 查询用户的所有权限 -->
<select id="queryAllPerms" resultType="string">
select m.perms from sys_user_role ur
LEFT JOIN sys_role_menu rm on ur.role_id = rm.role_id
LEFT JOIN sys_menu m on rm.menu_id = m.menu_id
where ur.user_id = #{userId}
</select>
</mapper>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Spring Boot+WebSocket+广播式</title>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">貌似你的浏览器不支持websocket</h2> </noscript>
<div>
<div>
<button id="connect" onclick="connect();">连接</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>
</div>
<div id="conversationDiv">
<label>输入你的名字</label><input type="text" id="name" />
<button id="sendName" onclick="sendName();">发送</button>
<p id="response"></p>
<p id="response1"></p>
</div>
</div>
<!--<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery.js}"></script>-->
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"> </script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script th:inline="javascript">
var stompClient = null;
//此值有服务端传递给前端,实现方式没有要求
var userId = 'admin';
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
$('#response').html();
}
function connect() {
var socket = new SockJS('/wx-monitor-server/websocket?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ3eC1tb25pdG9yLXNlcnZlciIsImlzcyI6InNoYW5nd2VpIiwiZXhwIjoxNTk0ODc5NDI3LCJpYXQiOjE1NjMzNDM0MjcsInVzZXIiOiJ7XCJ1c2VySWRcIjoyLFwiYWNjb3VudFwiOlwiYWRtaW5cIixcInJvbGVcIjoxfSJ9.WTb5VhEoPgCcGKKH9eIE8m5fsfPW3PXT7a8hWDHnTr0'); //1连接SockJS的endpoint是“endpointWisely”,与后台代码中注册的endpoint要一样。
stompClient = Stomp.over(socket);//2创建STOMP协议的webSocket客户端。
stompClient.connect({}, function(frame) {//3连接webSocket的服务端。
setConnected(true);
console.log('开始进行连接Connected: ' + frame);
stompClient.subscribe('/user/topic/room', function(respnose){
console.log(respnose);
});
stompClient.subscribe('/user/topic/login', function(respnose){
console.log(respnose);
});
});
}
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
var name = $('#name').val();
//通过stompClient.send()向地址为"/welcome"的服务器地址发起请求,与@MessageMapping里的地址对应。因为我们配置了registry.setApplicationDestinationPrefixes(Constant.WEBSOCKETPATHPERFIX);所以需要增加前缀/ws-push/
stompClient.send("/ws-push/welcome", {}, JSON.stringify({ 'name': name }));
}
function showResponse(message) {
var response = $("#response");
response.html(message);
}
function showResponse1(message) {
var response = $("#response1");
response.html(message);
}
</script>
</body>
</html>
package com.qkdata.test;
import com.qkdata.Application;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(value = "dev")
public class BaseTest {
}
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