工具idea , 模块化项目搭建
pom.xml 如下:
4.0.0
com.text
demo
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
2.0.4.RELEASE
此模块 pom.xml 如下:
demo
com.text
1.0-SNAPSHOT
4.0.0
demoAdmin
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
com.baomidou
mybatis-plus-boot-starter
3.0.5
com.baomidou
mybatis-plus
3.0.5
org.apache.velocity
velocity-engine-core
2.0
mysql
mysql-connector-java
8.0.11
org.springframework.boot
spring-boot-starter-data-redis
2.0.4.RELEASE
io.springfox
springfox-swagger2
2.8.0
io.springfox
springfox-swagger-ui
2.8.0
com.alibaba
fastjson
1.2.47
src/main/resources
src/main/resources/lib
BOOT-INF/lib
org.springframework.boot
spring-boot-maven-plugin
true
${project.name}
-Dfile.encoding=UTF-8
E:/compose/demoJar
common: 公共类配置
config: 配置文件
config.interceptor: 拦截器配置
controller: 控制器
dao: dao层
model.dto: 入参类
model.entity: 实体类
model.vo : 出参类
service : 接口类
service.impl: 接口实现类
shiro: 权限类
util: 工具类
AdminApplication.class : 启动入口
application.yml 如下图:
# 服务器配置 以自己的配置为主
server:
port: 8070
servlet:
context-path: /admin
mybatis-plus:
mapper-locations: classpath:/mapper/*.xml
type-aliases-package: com.demo.model.entity
logging:
config: classpath:logback-spring.xml
spring:
profiles:
#active: main # 正式环境
active: demo #测试环境
application-demo.yml 如下图:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
maximum-pool-size: 15
max-lifetime: 180000
idle-timeout: 60000
connection-timeout: 20000
minimum-idle: 5
redis:
port: 6379
host: 127.0.0.1
password:123456
banner.txt 如下图:
${AnsiColor.BRIGHT_YELLOW}
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 Admin 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////
${AnsiColor.BRIGHT_RED}
Application Version: ${application.version}${application.formatted-version}
Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}
logback-spring.xml 如下图:
%yellow(%d{yyyy-MM-dd HH:mm:ss}) [%-5level] [ %boldMagenta(%logger{50}) ] - %cyan(%msg%n)
${LOG_HOME}/admin.log
${LOG_HOME}/admin.log.%d{yyyy-MM-dd}.part%i.gz
2GB
10
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
ApiCommonController: 自定义父类 ,封装了自定义异常监控
BaseController: 自定义父类 ,封装了返回结果对象,获取ip
BaseDto: 分页类,用于传参对象继承使用
BusinessException: 自定义异常
CodeGeneration: mybatisPlus 代码生成器
Result: 返回结果对象
ApiCommonController.java 代码如下
package com.demo.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@Slf4j
public class ApiCommonController extends BaseController {
/**
* 抛出自定义BusinessException异常
*/
@ExceptionHandler({BusinessException.class})
@ResponseBody
public Result business(BusinessException ex) {
log.info(ex.toString());
return new Result(Result.ERROR,ex.getMessage());
}
protected void check(String code ,Object obj){
if (obj==null||obj.toString().trim().equals("null")||obj.toString().trim().length()==0) {
throw new BusinessException("参数[" +code+ "]不能为空");
}
}
}
BaseController.java 如下图:
package com.demo.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
@Slf4j
public class BaseController {
@Autowired
protected HttpServletRequest request;
/**
* 返回成功结果集
*/
protected Result ajaxDoneSuccess(Object data) {
return new Result(Result.SUCCESS, Result.SUCCESSMSG, data);
}
protected Result ajaxDoneSuccess(Object data,Integer total) {
return new Result(Result.SUCCESS, Result.SUCCESSMSG, data,total);
}
/**
* 返回成功结果
*/
protected Result ajaxDoneSuccess(String message) {
return new Result(Result.SUCCESS, message);
}
/**
* 返回成功结果和对象
*/
protected Result ajaxDoneSuccess(String message,Object data) {
return new Result(Result.SUCCESS, message,data);
}
/**
* 返回失败结果集
*/
protected Result ajaxDoneError(String message) {
return new Result(Result.ERROR, message);
}
/**
* 获取ip
*/
protected String getRealIp(){
String ip = request.getHeader("X-Real-IP");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("x-forwarded-for");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
BaseDto.java 代码如下图:
package com.demo.common;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class BaseDto {
@ApiModelProperty(value = "当前第几页")
private Integer pageIndex = 1;//当前第几页
@ApiModelProperty(value = "每页显示数")
private Integer pageSize = 10; //每页显示数
@ApiModelProperty(value = "开始时间 -查询")
private String beginTime;
@ApiModelProperty(value = "结束时间 -查询")
private String endTime;//结束时间 -查询
}
BusinessException.java 如下图:
package com.demo.common;
/**
* 自定义异常
*/
public class BusinessException extends RuntimeException {
public BusinessException(String msg){
super(msg);
}
}
Result.java 如下图:
package com.demo.common;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class Result {
public static final int SUCCESS = 200;
public static final int ERROR= 500;
public static final String SUCCESSMSG = "成功";
@ApiModelProperty(value = "状态码 200")
private int code = 0;
@ApiModelProperty(value = "说明信息")
private String msg = "";
@ApiModelProperty(value = "数据")
private Object data;
@ApiModelProperty(value = "数据总条数")
private Integer total=0; //条数
public Result(int code,String msg){
this.code=code;
this.msg=msg;
}
public Result(int code,String msg,Object data){
this.code=code;
this.msg=msg;
this.data=data;
}
public Result(int code,String msg,Object data ,Integer total){
this.code=code;
this.msg=msg;
this.data=data;
this.total=total;
}
}
CodeGeneration.java 代码生成器 如下图:
package com.demo.common;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
public class CodeGeneration {
/**
*
* @Title: main
* @Description: 文件生成器 运行main方法即可
* @param args
*/
public static void main(String[] args) {
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir("E://generation//temp");
gc.setFileOverride(true);
gc.setActiveRecord(false);// 不需要ActiveRecord特性的请改为false
gc.setEnableCache(false);// XML 二级缓存
gc.setBaseResultMap(true);// XML ResultMap
gc.setBaseColumnList(false);// XML columList
gc.setAuthor("ccm");// 作者
// 自定义文件命名,注意 %s 会自动填充表实体属性!
//gc.setControllerName("%sAction");
gc.setServiceName("%sService");
gc.setServiceImplName("%sServiceImpl");
gc.setMapperName("%sDao");
gc.setXmlName("%sMapper");
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setUrl("jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8");
mpg.setDataSource(dsc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
//strategy.setTablePrefix(new String[] { "sys_" });// 此处可以修改为您的表前缀
strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
//strategy.setInclude(new String[] { "sys_user" }); // 需要生成的表
strategy.setSuperServiceClass(null);
strategy.setSuperServiceImplClass(null);
strategy.setSuperMapperClass(null);
mpg.setStrategy(strategy);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.demo");
//pc.setController("controller");
pc.setService("service");
pc.setServiceImpl("service.serviceImpl");
pc.setMapper("dao");
pc.setEntity("model.entity");
pc.setXml("xml");
mpg.setPackageInfo(pc);
// 执行生成
mpg.execute();
}
}
HandlerInterceptorConfig: 拦截器
MyHandlerInterceptorConfig: 自定义拦截
corsFilter: 跨域
MybatisPlusConfig: MybatisPlus配置
RedisConfig: Redis配置
SwaggerConfig: Swagger2配置
HandlerInterceptorConfig.java 代码如下:
package com.demo.config.interceptor;
import com.demo.common.BusinessException;
import com.demo.config.RedisConfig;
import com.demo.shiro.ShiroAdmin;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class HandlerInterceptorConfig implements HandlerInterceptor {
@Autowired
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HttpSession session= request.getSession();
String keyName=RedisConfig.reids_prefix+session.getId();
ShiroAdmin shiroAdmin= (ShiroAdmin) redisTemplate.opsForValue().get(keyName);
if (shiroAdmin==null){
throw new BusinessException("当前账号未登录,请先登陆");
}
String token = request.getHeader("token");
if (token == null || token.trim().length() == 0) {
throw new BusinessException("token 禁止为空");
}
if (!shiroAdmin.getToken().equals(token)) {
throw new BusinessException("账号在其他地方登录,请重新登录");
}
shiroAdmin.setSessionId(" 被拦截 当前会话:"+session.getId());
request.getSession().setAttribute("shiroAdmin",shiroAdmin);
redisTemplate.expire(keyName,2,TimeUnit.HOURS);
return true;
}
}
CorsConfig.java 代码如下:
package com.demo.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 跨域配置
*/
@Configuration
public class CorsConfig {
// CORS
@Bean
FilterRegistrationBean corsFilter(@Value("*") String origin) {
return new FilterRegistrationBean(new Filter() {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String method = request.getMethod();
response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE");
response.setHeader("Access-Control-Max-Age", Long.toString(60 * 60));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "token,Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization");
if ("OPTIONS".equals(method)) {
response.setStatus(HttpStatus.OK.value());
} else {
chain.doFilter(req, res);
}
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
});
}
}
MyHandlerInterceptorConfig.java 代码如下:
package com.demo.config.interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyHandlerInterceptorConfig implements WebMvcConfigurer {
@Autowired
private HandlerInterceptor handlerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(handlerInterceptor).addPathPatterns("/**")
.excludePathPatterns("/login/**")
.excludePathPatterns("/*.html")
.excludePathPatterns("/webjars/**")
.excludePathPatterns("//swagger-resources/**");
}
}
MybatisPlusConfig.java 代码如下:
package com.demo.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan({"com.demo.dao"})
public class MybatisPlusConfig {
/**
* 性能优化 复杂查询可能会报错 建议关掉
* @return
*/
@Bean
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
/**/
performanceInterceptor.setMaxTime(1000);
/**/
performanceInterceptor.setFormat(false);
return performanceInterceptor;
}
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
RedisConfig.java 代码如下 :
package com.demo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
public final static String reids_prefix="demo_admin_" ; //redis前缀
@Bean
public RedisTemplate
SwaggerConfig.java 代码如下
package com.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
@EnableSwagger2
@Configuration
public class SwaggerConfig {
@Bean
public Docket createRestApi(){
ParameterBuilder ticketPar = new ParameterBuilder();
List pars = new ArrayList();
ticketPar.name("token").description("登录校验token")//name表示对象名称,description表示描述
.modelRef(new ModelRef("string")).parameterType("header")
.required(false).build();//required表示是否必填,defaultvalue表示默认值
pars.add(ticketPar.build());//添加完此处一定要把下边的带***的也加上否则不生效
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.demo.controller"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(pars);//************把消息头添加
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("测试接口")
.description("admin端接口")
.termsOfServiceUrl("none")
.version("1.0").build();
}
}
package com.demo.shiro;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 登陆对象
*/
@Data
public class ShiroAdmin {
@ApiModelProperty(value = "管理员id")
private Integer adminId;
@ApiModelProperty(value = "token")
private String token;
@ApiModelProperty(value = "登陆id")
private String loginCode;
@ApiModelProperty(value = "权限id")
private Integer roleId;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "会话id")
private String sessionId;
}
AuthManager.java 获取当前登陆的对象
package com.demo.shiro;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class AuthManager {
public static ShiroAdmin getShiroAdmin() {
return (ShiroAdmin)getSession().getAttribute("shiroAdmin");
}
public static String getSessionId(){
return getSession().getId();
}
public static HttpSession getSession(){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
return request.getSession();
}
}
package com.demo.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.demo.common.ApiCommonController;
import com.demo.common.Result;
import com.demo.config.RedisConfig;
import com.demo.model.dto.LoginDto;
import com.demo.model.entity.TSysAdmin;
import com.demo.service.TSysAdminService;
import com.demo.shiro.AuthManager;
import com.demo.shiro.ShiroAdmin;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/login")
@Api(description = "登陆注册")
public class LoginController extends ApiCommonController {
@Autowired
private TSysAdminService tSysAdminService;
@Autowired
private RedisTemplate redisTemplate;
@ApiOperation(value = "登陆", response = String.class)
@PostMapping("/loginDo")
public Result loginDo(@RequestBody LoginDto dto){
if (AuthManager.getSessionId()==null){
return ajaxDoneError("登陆异常,错误代码-10000");
}
TSysAdmin tSystemAdmin=tSysAdminService.getOne(new QueryWrapper().eq("login_code",dto.getLoginCode()).eq("pwd",dto.getPwd()));
if (tSystemAdmin==null){
return ajaxDoneError("账号或密码有误");
}
System.out.println("--------------------------------------");
ShiroAdmin shiroAdmin=new ShiroAdmin();
shiroAdmin.setAdminId(tSystemAdmin.getId());
shiroAdmin.setLoginCode(tSystemAdmin.getLoginCode());
shiroAdmin.setPhone(tSystemAdmin.getPhone());
shiroAdmin.setRoleId(tSystemAdmin.getRoleId());
shiroAdmin.setSessionId(AuthManager.getSessionId());
shiroAdmin.setToken(RandomStringUtils.randomAlphanumeric(24)+tSystemAdmin.getId());
String keyName=RedisConfig.reids_prefix+shiroAdmin.getSessionId();
redisTemplate.opsForValue().set(keyName,shiroAdmin,2,TimeUnit.HOURS);
return ajaxDoneSuccess("登陆成功 good",shiroAdmin.getToken());
}
}
package com.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement //开启事务
@ComponentScan(basePackages = {"com.demo.*"})
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class,args);
}
}
**
下载后 1:将CodeGeneration.java文件中的数据库连接改成实际的
2:将配置文件中的数据库,redis 改成实际的