中国加油,武汉加油!
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.0version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.54version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.7.0version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.7.0version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.48version>
dependency>
package com.wpj.config;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootConfiguration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.wpj.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("这里是springboot整合shiro和swagger前后分离的功能文档")
.description("这里是nz1904-springboot-08-shiro-swagger测试用的")
.version("v1.0")
.build();
}
}
package com.wpj.config;
import org.springframework.boot.SpringBootConfiguration;
@SpringBootConfiguration
public class ShiroConfig {
// 暂时不注入组件
}
package com.wpj.token;
import org.apache.shiro.authc.UsernamePasswordToken;
/**
* 对原生的token对象进行拓展
*/
public class CustomToken extends UsernamePasswordToken {
// 这个token是在认证通过之后 用户访问其他资源的时候 来进行你给身份识别的
private String token; // 用户身份唯一的标识
public CustomToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}
}
package com.wpj.exception;
/**
* 业务异常
*/
public class BusinessException extends RuntimeException {
private int messageCode;
private String defaultMessage;
public BusinessException(int messageCode, String defaultMessage){
super(defaultMessage);
this.messageCode = messageCode;
this.defaultMessage = defaultMessage;
}
public String getDefaultMessage() {
return defaultMessage;
}
public int getMessageCode() {
return messageCode;
}
}
package com.wpj.constant;
public class Constant {
public static final String REQ_TOKEN = "token";
}
package com.wpj.result;
import lombok.Data;
/**
* 最终响应给前端数据的对象
*/
@Data
public class DataResult <T> {
private int code; //返回的码值
private String msg; //返回的错误信息提示
private T data; //返回的数据
//下面这一块是对构造器进行封装
public DataResult(int code,T data){
this.code=code;
this.data=data;
this.msg=null;
}
public DataResult(int code,String msg,T data){
this.code=code;
this.data=data;
this.msg=msg;
}
public DataResult(int code,String msg){
this.code=code;
this.msg=msg;
}
public DataResult(){
this.code=BaseResponseCode.SUCCESS.getCode();
this.msg=BaseResponseCode.SUCCESS.getMsg();
this.data=null;
}
public DataResult(T data){
this.code=BaseResponseCode.SUCCESS.getCode();
this.msg=BaseResponseCode.SUCCESS.getMsg();
this.data=data;
}
public DataResult(ResponseCodeInterface responseCodeInterface){
this.data=null;
this.code=responseCodeInterface.getCode();
this.msg=responseCodeInterface.getMsg();
}
public DataResult(ResponseCodeInterface responseCodeInterface,T data){
this.data=data;
this.code=responseCodeInterface.getCode();
this.msg=responseCodeInterface.getMsg();
}
/**
* 不带数据的返回
* @param
* @return
*/
public static <T>DataResult success(){
return new DataResult();
}
/**
* 带数据的返回
* @param data
* @param
* @return
*/
public static <T>DataResult success(T data){
return new DataResult(data);
}
/**
* 自己给参数的问题
* @param code
* @param msg
* @param data
* @param
* @return
*/
public static <T>DataResult getResult(int code,String msg,T data){
return new <T>DataResult(code,msg,data);
}
/**
* 自己给参数的问题
* @param code
* @param msg
* @param
* @return
*/
public static <T>DataResult getResult(int code,String msg){
return new <T>DataResult(code,msg);
}
/**
* 直接传递一个枚举进来
* @param baseResponseCode
* @param
* @return
*/
public static <T>DataResult getResult(BaseResponseCode baseResponseCode){
return new <T>DataResult(baseResponseCode);
}
/**
* 直接传递一个枚举进来
* @param baseResponseCode
* @param
* @return
*/
public static <T>DataResult getResult(BaseResponseCode baseResponseCode,T data){
return new <T>DataResult(baseResponseCode,data);
}
}
package com.wpj.result;
public enum BaseResponseCode implements ResponseCodeInterface{
/**
* 接下来就要和前端约束好所有的码值以及含义
*/
SUCCESS(0,"操作成功"),
SYSTEM_ERROR(5000001,"系统错误"),
METHOD_INVALIDATE(4000001,"数据校验出错"),
DATA_ERROR(4000002,"传入数据异常"),
TOKEN_NOT_NULL(4010001,"用户认证异常");
//整个构造函数
BaseResponseCode(int code,String msg){
this.code=code;
this.msg=msg;
}
private int code;
private String msg;
@Override
public int getCode() {
return code;
}
@Override
public String getMsg() {
return msg;
}
}
package com.wpj.result;
/**
* 返回码的一个接口
*/
public interface ResponseCodeInterface {
/**
* 返回的码的一个获取
* @return
*/
int getCode();
/**
* 返回消息的获取
* @return
*/
String getMsg();
}
package com.wpj.filter;
import com.alibaba.fastjson.JSONObject;
import com.wpj.constant.Constant;
import com.wpj.exception.BusinessException;
import com.wpj.token.CustomToken;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 对身份进行二次校验的时候就会进来这里
*/
public class CustomAccessControllerFilter extends AccessControlFilter {
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletRequest request = (HttpServletRequest) servletRequest;
// 校验身份
try {
// 获取token
String token = request.getHeader(Constant.REQ_TOKEN);
// 判断token是否为空
if (StringUtils.isEmpty(token)) {
// 身份非法
throw new BusinessException(400001, "用户的请求token不能为空。");
} else {
// 封装token交给shiro去认证,查看是否合法
CustomToken customToken = new CustomToken(token);
// 用户第一次登陆并不会执行,是在认证成功之后访问其他资源的时候才执行
// login最终会执行到Realm中的doGetAuthenticationInfo方法里
getSubject(servletRequest, servletResponse).login(customToken);
}
} catch (BusinessException e) {
// 返回json格式信息通知前端出问题了
resultResponse(e.getMessageCode(), e.getDefaultMessage(), servletResponse);
} catch (AuthenticationException e) {
if(e.getCause() instanceof BusinessException) {
// 将异常的实例进行转换
BusinessException err = (BusinessException) e.getCause();
resultResponse(err.getMessageCode(), err.getDefaultMessage(), servletResponse);
} else {
// 执行这里就说明这个异常是shiro返回的
resultResponse(400001, "用户认证失败", servletResponse);
}
} catch (AuthorizationException e) {
if(e.getCause() instanceof BusinessException) {
// 将异常的实例进行转换
BusinessException err = (BusinessException) e.getCause();
resultResponse(err.getMessageCode(), err.getDefaultMessage(), servletResponse);
} else {
// 执行这里就说明这个异常是shiro返回的
resultResponse(403001, "用户没有访问权限", servletResponse);
}
} catch (Exception e) { // 一些未考虑的异常
if(e.getCause() instanceof BusinessException) {
// 将异常的实例进行转换
BusinessException err = (BusinessException) e.getCause();
resultResponse(err.getMessageCode(), err.getDefaultMessage(), servletResponse);
} else {
// 执行这里就说明这个异常是shiro返回的
resultResponse(500001, "系统出现异常", servletResponse);
}
}
return true;
}
/**
* 通知前端出错的信息
* @param messageCode
* @param defaultMessage
* @param response
*/
private void resultResponse(int messageCode, String defaultMessage, ServletResponse response) {
// 构建返回的数据
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", messageCode);
jsonObject.put("msg", defaultMessage);
// 设置下返回类型
response.setContentType(MediaType.APPLICATION_JSON_UTF8.toString());
// 获取输出流
try {
ServletOutputStream out = response.getOutputStream();
out.write(jsonObject.toJSONString().getBytes());
out.flush();
}catch (IOException e) {
e.printStackTrace();
}
}
}
package com.wpj.filter;
import com.wpj.exception.BusinessException;
import com.wpj.service.IUserService;
import com.wpj.token.CustomToken;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.springframework.beans.factory.annotation.Autowired;
public class CustomHashedCredentialsMatcher extends HashedCredentialsMatcher {
@Autowired
private IUserService userService;
/**
* 下面这个方法 返回true 或者 false就决定了 这个是成功呢还是失败
* @param token
* @param info
* @return
*/
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
// 把前面传递过来的token取出来 再把存储到服务器的token取出来做比较
// 如果一致那么就返回true 否则就返回 false
CustomToken token1= (CustomToken) token;
// 取出 Principal的值 (下面这个值 就是从前端传递过来进行比较的)
String tokenVal= (String) token1.getPrincipal();
// 从redis 或者 数据库 或者 session取出这个信息来
boolean flag=false;
try{
flag = userService.tokenExistsOrNot(tokenVal);
}catch (Exception err){
throw new BusinessException(500001,"查询token存在失败"+err.getMessage());
}
//进行比较 判定前端的token和服务端的token是否一致 如果一致 那么返回true 否则返回false
if(!flag){
throw new BusinessException(4010000,"授权信息无效请重新登录");
}
return true;
}
}
package com.wpj.realm;
import com.wpj.token.CustomToken;
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;
public class CustomRealm extends AuthorizingRealm {
/**
* 授权的方法
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
return simpleAuthorizationInfo;
}
/**
* 认证的方法
* 将前端放进去的token 取出来 放到校验的SimpleAuthenticationInfo中去 方便后面进行校验
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//取出前端传递过来的token
CustomToken customToken= (CustomToken) authenticationToken;
String token = (String) customToken.getPrincipal();
//在这里要将前端传递过来的token给封装到 SimpleAuthenticationInfo 对象中去
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(token,token,getName());
return simpleAuthenticationInfo;
}
}
package com.wpj.config;
import com.wpj.filter.CustomAccessControllerFilter;
import com.wpj.filter.CustomHashedCredentialsMatcher;
import com.wpj.realm.CustomRealm;
import org.apache.shiro.mgt.DefaultSecurityManager;
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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
@SpringBootConfiguration
public class ShiroConfig {
/**
* 项目认证(请求资源的时候 身份的认证)的realm
* @return
*/
@Bean
public CustomRealm customRealm(){
CustomRealm customRealm = new CustomRealm();
customRealm.setCredentialsMatcher(customHashedCredentialsMatcher());
return customRealm;
}
/**
* 凭证匹配器的申明
* @return
*/
@Bean
public CustomHashedCredentialsMatcher customHashedCredentialsMatcher(){
return new CustomHashedCredentialsMatcher();
}
/**
* 安全管理器
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(customRealm());
return securityManager;
}
/**
* 配置的是目标过滤器的代理
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//配置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//接下来配置些过滤器
//配置自己的那个过滤器
Map<String, Filter> map = new LinkedHashMap<>();
map.put("token",new CustomAccessControllerFilter());
shiroFilterFactoryBean.setFilters(map);
//对请求过滤和拦截的设置
Map<String,String> reqMap=new LinkedHashMap<>();
//放入不拦截的页面 拦截的页面....
reqMap.put("/user/login","anon");
//Swagger的所有请求的资源和请求的地址都不需要拦截
reqMap.put("/swagger/**","anon");
reqMap.put("/v2/api-docs","anon");
reqMap.put("/swagger-ui.html","anon");
reqMap.put("/swagger-resources/**","anon");
reqMap.put("/webjars/**","anon");
reqMap.put("/favicon.ico","anon");
reqMap.put("/captcha.jpg","anon");
reqMap.put("/csrf","anon");
//设置我们自己的校验
reqMap.put("/**","token,authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(reqMap);
return shiroFilterFactoryBean;
}
/**
* 开启aop的注解的支持
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager){
AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
attributeSourceAdvisor.setSecurityManager(securityManager);
return attributeSourceAdvisor;
}
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
}
package com.wpj.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
// 在User类名上alt+/ 自己生成
private static final long serialVersionUID = 8434196542261610760L;
private Integer id;
private String name;
private String pwd;
private String token;
private Date expireDate; //token的过期时间
}
package com.wpj.vo;
import com.wpj.pojo.User;
/**
* 横向扩展
*/
public class CustomUser extends User {
}
package com.wpj.vo;
/**
* 纵向扩展
*/
public class UserVo {
private CustomUser customUser;
}
package com.wpj.mapper;
import com.wpj.pojo.User;
import java.util.List;
/**
* 用户的DAO接口
*/
public interface IUserMapper {
/**
* 通过名字找到用户
* @param userName
* @return
*/
User findUserByName(String userName);
/**
* 更新token到数据库
* @param user
*/
void updateToken(User user);
/**
* 查询所有的用户
* @return
*/
List<User> findUserList();
/**
* 查看当前的token是否在数据库中存在
* @param token
* @return
*/
User findUserByToken(String token);
}
<mapper namespace="com.wpj.mapper.IUserMapper">
<select id="findUserByName" parameterType="string" resultType="user">
select * from user where name=#{value}
select>
<update id="updateToken" parameterType="user">
update user set token=#{token} where id=#{id}
update>
<select id="findUserList" resultType="user">
select * from user
select>
<select id="findUserByToken" parameterType="String" resultType="user">
select * from user where token=#{value}
select>
mapper>
mybatis.type-aliases-package=com.wpj.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///nz1904-shiro-swagger
spring.datasource.username=root
spring.datasource.password=123456
package com.wpj.service;
import com.wpj.pojo.User;
import java.util.List;
public interface IUserService {
/**
* 登陆
* @param user
* @return
*/
User login(User user);
/**
* 通过名字找到用户
* @param userName
* @return
*/
User findUserByName(String userName);
/**
* 更新token到数据库
* @param user
*/
void updateToken(User user);
/**
* 查询所有的用户
* @return
*/
List<User> findUserList()throws Exception;
/**
* 判定这个token是否存在
* @param token
* @return
*/
boolean tokenExistsOrNot(String token);
}
package com.wpj.service.impl;
import com.wpj.exception.BusinessException;
import com.wpj.mapper.IUserMapper;
import com.wpj.pojo.User;
import com.wpj.result.DataResult;
import com.wpj.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Service
@Transactional
public class UserServiceImpl implements IUserService {
@Autowired
private IUserMapper iUserMapper;
@Override
public User login(User user){
//这个类里面应该干什么?
/**第一步:获取到前端传递过来的用户名
*第二步:通过用户名 获取用户对象
* 第三步:校验
* 第四步:生成token保存到数据库
* 第五步:将token封装到返回数据里面给前端
*/
//获取用户名
String userName = user.getUserName();
//通过用户名 找用户名找对象
User userResult = findUserByName(userName);
//第三步:校验
if(null==userResult){ //说明用户名不对
throw new BusinessException(40001,"用户名不对");
}
//说明:用户名是对的
//比较密码
if(!(userResult.getPassword().equals(user.getPassword()))){
throw new BusinessException(40002,"密码不对");
}
//执行到这里说明用户身份合法的
//先将数据保存到一个类里面
//首先要生成token这个值
String token= UUID.randomUUID().toString();
Date date=new Date();
//设置这个值给user对象
userResult.setToken(token);
userResult.setExpireDate(date);
//下面就是更新这个数据库的数据
updateToken(userResult);
//将这个信息返回给前端
//一般情况下 密码是不需要返回的
userResult.setPassword("");
//设置返回数据的对象
//DataResult userDataResult = new DataResult<>(0,"认证成功",userResult);
return userResult;
}
@Override
public User findUserByName(String userName) {
return iUserMapper.findUserByName(userName);
}
@Override
public void updateToken(User user) {
iUserMapper.updateToken(user);
}
@Override
public List<User> findUserList() throws Exception{
List<User> userList = iUserMapper.findUserList();
//接下来对数据进行封装
DataResult<List<User>> dataResult = new DataResult<>(0, "请求完美", userList);
return userList;
}
@Override
public boolean tokenExistsOrNot(String token) {
//通过token查询用户信息
User userResult = iUserMapper.findUserByToken(token);
//接下来就要判断了
if(null!=userResult){
return true;
}
return false;
}
}
package com.wpj.controller;
import com.wpj.exception.BusinessException;
import com.wpj.pojo.User;
import com.wpj.result.BaseResponseCode;
import com.wpj.result.DataResult;
import com.wpj.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@Api(tags = {"用户接口"})
public class UserController {
@Autowired
private IUserService userService;
private Logger logger= LoggerFactory.getLogger(UserController.class);
/**
* 登陆的接口
* @param user
* @return
*/
@RequestMapping(value = "/user/login",method = RequestMethod.POST)
@ApiOperation(value = "用户登陆的接口")
public DataResult<User> login(@RequestBody User user){
//这个里面应该干什么?
/**
* 说白了 调用业务逻辑层的方法
* 异常的捕获
* 返回数据
*/
DataResult<User> dataResult=null;
try {
User user1 = userService.login(user);
dataResult=DataResult.success(user1);
} catch (Exception e) {
if(e instanceof BusinessException){ //说明是业务异常
BusinessException err= (BusinessException) e;
//应该干什么?
dataResult=new DataResult<>(err.getMessageCode(),err.getDefaultMessage());
}else{
//dataResult=new DataResult<>(500001,"系统异常造成登陆失败");
dataResult=DataResult.getResult(BaseResponseCode.SYSTEM_ERROR.getCode(),BaseResponseCode.SYSTEM_ERROR.getMsg());
}
return dataResult;
}
return dataResult;
}
/**
* 查找所有的用户数据
* @return
*/
@RequestMapping(value = "/user/list",method = RequestMethod.GET)
@ApiOperation(value = "获取所有的用户信息")
@ApiImplicitParam(paramType = "header",name = "token",value = "用户token",required = true,dataType = "String")
public DataResult<List<User>> findUserList(){
//定义返回数据
DataResult<List<User>> userLists;
try{
//返回用户数据
List<User> users = userService.findUserList();
userLists=DataResult.success(users);
logger.info("获取数据成功....");
}catch (Exception err){
//说明获取信息失败了
logger.error("获取用户信息失败:"+err.fillInStackTrace());
userLists=DataResult.getResult(BaseResponseCode.SYSTEM_ERROR.getCode(),BaseResponseCode.SYSTEM_ERROR.getMsg());
}
return userLists;
}
}
/**
* 全局的配置文件
*/
@SpringBootConfiguration
@ComponentScan(basePackages = {"com.wpj"})
@MapperScan(basePackages = {"com.wpj.mapper"})
public class AppConfig {
}