- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
Springboot + vue + dm8 的前后端分离项目,后端项目 。
https://spring.io
https://start.aliyun.com
初始化项目,导入坐标 。
web、lombok、devtools
<!-- web start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- web end -->
<!-- devtools start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- devtools end -->
<!-- lombok start -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- lombok end -->
<swagger3.version>3.0.0</swagger3.version>
<!-- swagger3 start -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${swagger3.version}</version>
</dependency>
<!-- swagger3 end -->
package org.cn.common.config;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Configuration
@EnableOpenApi
@EnableWebMvc
public class Swagger3Config {
/**
* 创建API
* http:localhost:9999/swagger-ui/index.html 原生地址
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.OAS_30).pathMapping("/")
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
/*.enable(enable)*/
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有有注解的api,用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解
//.apis(RequestHandlerSelectors.basePackage("com.cn"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.regex("(?!/ApiError.*).*"))
.paths(PathSelectors.any())
.build()
// 支持的通讯协议集合
.protocols(newHashSet("https", "http"))
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
/**
* 支持的通讯协议集合
*
* @param type1
* @param type2
* @return
*/
private Set<String> newHashSet(String type1, String type2) {
Set<String> set = new HashSet<>();
set.add(type1);
set.add(type2);
return set;
}
/**
* 认证的安全上下文
*/
private List<SecurityScheme> securitySchemes() {
List<SecurityScheme> securitySchemes = new ArrayList<>();
securitySchemes.add(new ApiKey("Authorization", "Authorization", "header"));
return securitySchemes;
}
/**
* 授权信息全局应用
*/
private List<SecurityContext> securityContexts() {
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.any()).build());
return securityContexts;
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
}
/**
* 添加摘要信息
* @return 返回ApiInfo对象
*/
private ApiInfo apiInfo() {
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("接口文档")
// 服务条款
.termsOfServiceUrl("NO terms of service")
// 描述
.description("权限模型管理系统-接口文档")
// 作者信息
.contact(new Contact("LM", "https://www.cnblogs.com/longronglang/", "lumin@gmail.com"))
// 版本
.version("版本号:V1.0")
//协议
.license("The Apache License")
// 协议url
.licenseUrl("https://www.apache.org/licenses/LICENSE-2.0.html")
.build();
}
}
新增测试类、测试方法等,加上Swagger3注解进行测试 。
http:localhost:9999/swagger-ui/index.html 。
@Api(tags = "用户管理")
@ApiOperation("用户登录")
http://localhost:9999/swagger-ui/index.html
mybatis-plus-boot-starter、mybatis-plus-generator、freemarker、dameng
注意:mybatis-plus 坐标引入的是mybatis-plus启动坐标和代码生成坐标 。
<dm.version>8.1.2.192</dm.version>
<mybatis-plus-boot.version>3.5.3.1</mybatis-plus-boot.version>
<mybatis-plus-generator.version>3.5.3.1</mybatis-plus-generator.version>
<!-- mybatis-plus-boot start -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot.version}</version>
</dependency>
<!-- mybatis-plus-boot end -->
<!-- mybatis-plus-generator start -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus-generator.version}</version>
</dependency>
<!-- mybatis-plus-generator end -->
<!-- freemarker start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- freemarker end -->
<!-- dameng start -->
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>${dm.version}</version>
</dependency>
<!-- dameng end -->
package generator;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections;
public class CodeGenerator {
static String url ="jdbc:dm://LOCALHOST:5236";
static String username ="SYSDBA";
static String password ="SYSDBA001";
static String moduleName ="system";
static String javaLocation ="F:\\install\\workspace\\sty\\x-dm-server\\src\\main\\java";
static String mapperLocation ="F:\\install\\workspace\\sty\\x-dm-server\\src\\main\\resources\\mapper\\";
static String tables = "x_user,x_role,x_menu,x_user_role,x_role_menu";
public static void main(String[] args) {
FastAutoGenerator.create(url,username,password)
.globalConfig(builder -> builder
.author("LuMin")
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir(javaLocation)
.commentDate("yyyy-MM-dd")
)
.packageConfig(builder -> builder
.parent("org.cn") // 设置父包名
.moduleName(moduleName) // 设置父包模块名
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.pathInfo(Collections.singletonMap(OutputFile.xml, mapperLocation))
)
.strategyConfig(builder -> builder
.addInclude(tables) // 设置需要生成的表名
.addTablePrefix("x_") // 设置过滤表前缀
.entityBuilder()
.enableLombok()
)
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
public static class Test {
static Connection con = null;
static String cname = "dm.jdbc.driver.DmDriver";
static String url = "jdbc:dm://192.168.106.137:30236";
static String userid = "SYSDBA";
static String pwd = "SYSDBA001";
public static void main(String[] args) {
try {
Class.forName(cname);
con = DriverManager.getConnection(url, userid, pwd);
con.setAutoCommit(true);
System.out.println("[SUCCESS]conn database");
} catch (Exception e){
System.out.println("[FAIL]conn database:" + e.getMessage());
}
}
public void disConn(Connection con) throws SQLException {
if (con != null) {
con.close();
}
}
}
}
# 注意生成代码之后,直接启动项目会报错,需要对项目信息进行修改
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'menuServiceImpl': Unsatisfied dependency expressed through field 'baseMapper'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.cn.system.mapper.MenuMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
# ①修改启动类扫描的包
@SpringBootApplication(scanBasePackages = {"org.cn"})
@MapperScan(value = {"org.cn.*.mapper"})
# ②在Mapper接口上增加@Mapper类注解,启动还是会报错
initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'menuServiceImpl': Unsatisfied dependency expressed through field 'baseMapper'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'menuMapper' defined in file
# ③删除创建项目默认生成的demos包,重新启动
# server
server:
port: 9999
# datasource
spring:
datasource:
url: jdbc:dm://LOCALHOST:5236/XGIS
username: SYSDBA
password: SYSDBA001
driver-class-name: dm.jdbc.driver.DmDriver
jpa:
database-platform: org.hibernate.dialect.DMDialect
hibernate:
ddl-auto: update # 或者 create, create-drop, validate, none
show-sql: true # 显示执行的 SQL 语句,便于调试
properties:
hibernate:
format_sql: true # 格式化 SQL 语句输出
redis:
port: 6379
password: Admin@123
host: localhost
# mybatis-plus
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
logic-delete-field: deleted
logic-not-delete-value: 0
logic-delete-value: 1
mapper-locations: classpath*:mapper/*.xml
type-aliases-package: org.cn
# logging
logging:
charset:
console: utf-8
level:
org.cn: debug
利用SpringBootJunit测试工具进行测试,在XDmServerApplicationTests类中新建testUserMapper()方法 。
package org.cn;
import org.cn.system.entity.User;
import org.cn.system.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class XDmServerApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
}
@Test
void testUserMapper () {
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
foreach循环时没有打印出预期的内容(例如用户对象的某些属性),而是打印出了类似org.cn.system.entity.User@1ad1c363这样的内容,这通常意味着你没有正确覆写User类的toString方法.
org.cn.system.entity.User@76d0ecd7
org.cn.system.entity.User@57c69937
org.cn.system.entity.User@1ad1c363
org.cn.system.entity.User@446b64b3
org.cn.system.entity.User@35ac9ebd
org.cn.system.entity.User@56c0a61e
一是在User实体类中增加 toString()方法 。
二是在实体类上添加@Data/@NoArgsConstructor/@AllArgsConstructor注解 。
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", status='" + status + '\'' +
", phone=" + phone +
", avatar='" + avatar + '\'' +
", deleted=" + deleted +
'}';
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
/**
* 返回编码
*/
private Integer code;
/**
* 返回信息
*/
private String message;
/**
* 返回数据
*/
private T data;
/**
* 返回成功
* @return
* @param <T>
*/
public static <T> Result<T> success(){
return new Result<>(20000, "success",null);
}
public static <T> Result<T> success(T data){
return new Result<>(20000, "success",data);
}
public static <T> Result<T> success(T data,String message){
return new Result<>(20000, message,data);
}
public static <T> Result<T> success(String message){
return new Result<>(20000, message,null);
}
/**
* 返回失败
* @return
* @param <T>
*/
public static <T> Result<T> fail(){
return new Result<>(20001, "fail",null);
}
public static <T> Result<T> fail(Integer code){
return new Result<>(20001, "fail",null);
}
public static <T> Result<T> fail(Integer code,String message){
return new Result<>(20001, message,null);
}
public static <T> Result<T> fail(String message){
return new Result<>(20001, message,null);
}
}
修改控制器,测试公共响应类是否返回自定义数据 。
修改UserController控制器的@Controller为@RestController
在控制类中注入IUserService接口服务类,新增getUserList()测试方法进行测试 。
@Resource
private IUserService userService;
@GetMapping("/getUserList")
public Result<List<User>> getUserList() {
List<User> userList = userService.list();
return Result.success(userList, "查询成功");
}
@Getter/@Setter是mybatis-plus-generator生成实体时自动添加的注解 。
@Getter
@Setter
@Data/@NoArgsConstructor/@AllArgsConstructor是lombok中的注解 。
@Data
@NoArgsConstructor
@AllArgsConstructor
spring-security-core、jjwt
<fastjson.version>2.0.1</fastjson.version>
<!-- spring-security-core start -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<!-- spring-security-core end -->
<!-- jjwt start -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<!-- jjwt end -->
①在application.yml配置JWT令牌和有效期 。
# JWT 配置
jwt:
secret-key: 123456
token-validity-in-seconds: 86400000
②JwtUtil工具类 。
@Component
@Slf4j
public class JwtValidateInterceptor implements HandlerInterceptor {
@Resource
private JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// String token = request.getHeader("Authorization");
/*request.getHeader()方法与集成Swagger中ApiKey并无多大关系
private List<SecurityScheme> securitySchemes() {
List<SecurityScheme> securitySchemes = new ArrayList<>();
securitySchemes.add(new ApiKey("Authorization", "Authorization", "header"));
return securitySchemes;
}*/
String token = request.getHeader("X-Token");
log.debug(request.getRequestURI()+"需要验证"+token);
if (token != null) {
try {
jwtUtil.parseToken(token);
log.debug(request.getRequestURI()+"验证通过");
return true;
} catch (Exception e) {
throw new Exception(e);
}
}
log.debug(request.getRequestURI()+"验证失败,禁止访问");
response.setContentType("application/json;charset=utf-8");
Result<Object> fail = Result.fail(20003, "jwt验证失败,请重新登录");
response.getWriter().write(JSON.toJSONString(fail));
return false; //拦截
}
}
@Configuration
public class JwtInterceptorConfig implements WebMvcConfigurer {
@Resource
private JwtValidateInterceptor jwtValidateInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(jwtValidateInterceptor);
registration.addPathPatterns("/**")
.excludePathPatterns(
"/system/user/login",
"/system/user/info",
"/system/user/logout",
"/swagger-ui/**",
"/swagger-resources/**",
"/v3/**",
"/error",
"/webjars/**",
"/v2/**",
"/public/**",
"/static/**",
"*.html",
"doc.html",
"/favicon.ico"
);
}
}
①在 XAdminServerApplicationTests 测试类中注入 。
@Autowired
private JwtUtil jwtUtil;
在 XAdminServerApplicationTests 测试
②测试Jwt创建 。
/**
* Jwt创建
*/
@Test
void testCreateJwt(){
User user = new User();
user.setUsername("zhangsan");
user.setPhone(18722820382L);
String token = jwtUtil.createToken(user);
System.out.println(token);
}
③测试Jwt解析 。
/**
* Jwt解析
*/
@Test
void testParseJwt(){
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJkZWRiNTRiMC1kOTllLTRhZGEtOGFjMi1iZTA2ODRlNzVhZmYiLCJzdWIiOiJ7XCJ0ZWxlcGhvbmVcIjoxODcyMjgyMDM4MixcInVzZXJuYW1lXCI6XCJ6aGFuZ3NhblwifSIsImlzcyI6InN5c3RlbSIsImlhdCI6MTcwNjExMzU3MCwiZXhwIjoxNzA2MTE1MzcwfQ.Pcj8kjcAC8FW-PER65wiYYJ1bSptOG8mViIHyeIY3EI";
Claims claims = jwtUtil.parseToken(token);
System.out.println("claims = " + claims);
}
认证过程可以参考此篇博客,写得非常详细 。
https://blog.csdn.net/liuerchong/article/details/108606650 。
在IUserService服务类中新增用户登录业务逻辑接口 。
/**
* 用户登录业务逻辑接口
* @param user
* @return
*/
Map<String, Object> login(User user);
第一步根据用户名和密码查询用户信息; 。
第二步判断查询结果是否为空,为空抛出异常信息,不为空则将封装的登录信息传入JwtUtil中生成token 。
根据前端传入User对象,LambdaQueryWrapper 构造wrapper查询对象参数,再将wrapper参数传入后端baseMapper查询接口,查询用户信息是否存在.
// 1. 根据用户名查询用户信息
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername,user.getUsername());
User loginUser = this.baseMapper.selectOne(wrapper);
第一步:判断用户信息是否为空 。
判断用户信息不为空,使用spring-security-core中PasswordEncoder进行加密并使用matches方法匹配后端查询出来的密码是否一致,这里需要注意一下,安全考虑,密码在传输过程中需要进行置空,不要放到JWT生成token中,PasswordEncoder需要配置成一个公用组件,不然会报错。PasswordEncoder组件配置如下:
@Configuration
public class PasswordEncoderConfig {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
第二步:不为空,则创建token 。
①定义一个字符串token,用于接收jwtUtil中生成的token值(实质是JwtBuilder中将AES加密令牌密钥和过期时间压缩的字符串); 。
②将封装的loginUser登录信息传入JwtUtil工具类中createToken方法中生成token; 。
③定义一个Map集合,将jwtUtil生成的token存储在Map集合中,返回出去 。
// 2. 结果不为空,则生成 token
if (loginUser != null && passwordEncoder.matches(user.getPassword(),loginUser.getPassword())) {
String key = "user" + UUID.randomUUID();
loginUser.setPassword(null);
String token = jwtUtil.createToken(loginUser);
Map<String,Object> data = new HashMap<>();
data.put("token",token);
return data;
}
第三步:JwtUtil工具生成token步骤 。
①在application.yml配置JWT令牌和有效期 。
# JWT 配置
jwt:
secret-key: 123456
token-validity-in-seconds: 86400
②创建JWT工具类,通对令牌密钥进行Base64编码,再将编码后的令牌密码进行AES加密,并设置令牌密码过期时间,过期时间等于当前时间加上application.yml中设置的过期时间,最后将加密后的令牌密钥和过期时间压缩成紧凑字符串返回出去.
/*
* @Description: JwtUtils工具类
**/
@Component
public class JwtUtil {
// 有效期
@Value("${jwt.token-validity-in-seconds}")
private Long JWT_EXPIRE;
// 令牌密钥
@Value("${jwt.secret-key}")
private String JWT_KEY;
public String createToken(Object data){
// 当前时间
Long currentTime = System.currentTimeMillis();
// 过期时间
Long expTime = currentTime+JWT_EXPIRE;
//jwt
JwtBuilder builder = Jwts.builder()
.setId(UUID.randomUUID()+"")
.setSubject(JSON.toJSONString(data))
.setIssuer("acme") // 设置JWT的发布者信息
.setIssuedAt(new Date(currentTime))
// 签名方法
.signWith(SignatureAlgorithm.HS256,encodeSecret(JWT_KEY))
.setExpiration(new Date(expTime));
return builder.compact();
}
private SecretKey encodeSecret(String key) {
byte[] encode = Base64.getEncoder().encode(key.getBytes());
SecretKeySpec aes = new SecretKeySpec(encode,0,encode.length,"AES");
return aes;
}
public Claims parseToken (String token) {
Claims body = Jwts.parser()
.setSigningKey(encodeSecret(JWT_KEY))
.parseClaimsJws(token)
.getBody();
return body;
}
public <T> T parseToken (String token,Class<T> clazz) {
Claims body = Jwts.parser()
.setSigningKey(encodeSecret(JWT_KEY))
.parseClaimsJws(token)
.getBody();
return JSON.parseObject(body.getSubject(),clazz);
}
}
在IUserService新增获取当前用户信息业务逻辑接口 。
/**
* 获取当前用户信息业务逻辑接口
* @param token
* @return
*/
Map<String, Object> getCurrentUserInfo(String token);
在UserServiceImpl业务逻辑接口实现类中实现获取当前用户信息接口 。
@Override
public Map<String, Object> getCurrentUserInfo(String token) {
// 1. 根据 token 获取用户信息
User loginUser = null;
try {
loginUser = jwtUtil.parseToken(token,User.class);
} catch (Exception e) {
e.printStackTrace();
}
if (loginUser != null) {
Map<String, Object> data = new HashMap<>();
// 用户信息
data.put("username", loginUser.getUsername());
data.put("avatar", loginUser.getAvatar());
// 角色
// 权限列表
// 返回用户信息数据
return data;
}
return null;
}
在UserController控制器新增获取用户信息方法,调用获取用户信息业务逻辑实现接口实现方法 。
/**
* 获取当前用户信息
* @param token
* @return
*/
@ApiOperation("当前用户信息")
@GetMapping("/info")
public Result<Map<String,Object>> getCurrentUserInfo(@RequestParam("token") String token){
// 根据 token 获取用户信息
Map<String,Object> data = userService.getCurrentUserInfo(token);
if (data != null) {
return Result.success(data);
}
return Result.fail(20003,"用户登录无效,请重新登录");
}
在IUserService新增用户登录业务逻辑接口 。
/**
* 注销登录
* @param token
* @return
*/
@PostMapping("logout")
public Result<?> logout(@RequestHeader("token") String token){
userService.logout(token);
return Result.success();
}
在UserServiceImpl业务逻辑接口实现类中实现注销登录接口 。
@Override
public void logout(String token) {
}
在UserController控制器新增注销登录方法,调用注销登录业务逻辑实现接口实现方法 。
/**
* 注销登录
* @param token
* @return
*/
@PostMapping("logout")
public Result<?> logout(@RequestHeader("token") String token){
userService.logout(token);
return Result.success();
}
解决前后端跨越问题 。
Access to XMLHttpRequest at 'http://localhost:9999/system/user/login' from origin
'http://localhost:9528' has been blocked by CORS policy: Response to preflight request
doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
# CorsUrl
corsUrl:
url: http://localhost:9528
package org.cn.common.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/*
* @Description: 跨域后端拦截器配置类
**/
@Configuration
public class CorsConfig {
// 允许前端服务访问后端接口
@Value("${corsUrl.url}")
private String CorsUrl;
@Bean
public CorsFilter corsFilter() {
CorsConfiguration configuration = new CorsConfiguration();
// 允许什么网址来异步访问
configuration.addAllowedOrigin(CorsUrl);
// 获取cookie
configuration.setAllowCredentials(true);
// 允许什么方法? POST、GET,此处为* 意味全部允许
configuration.addAllowedMethod("*");
// 允许所有的请求头
configuration.addAllowedHeader("*");
// 设置资源过滤器,过滤什么资源
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",configuration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}
package org.cn.common.config;
import org.cn.common.interceptor.JwtValidateInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Configuration
public class JwtInterceptorConfig implements WebMvcConfigurer {
@Resource
private JwtValidateInterceptor jwtValidateInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(jwtValidateInterceptor);
registration.addPathPatterns("/**")
.excludePathPatterns(
"/system/user/login",
"/system/user/info",
"/system/user/logout",
"/swagger-ui/**",
"/swagger-resources/**",
"/v3/**",
"/error",
"/webjars/**",
"/v2/**",
"/public/**",
"/static/**",
"*.html",
"doc.html",
"/favicon.ico"
);
}
}
前端登录,查看页面控制台是否还报跨越的问题
实现分页查询,需要Mybatis Plus中分页插件配置成分页拦截器组件 。
package org.cn.common.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
* @Description: MybatisPlusConfig分页拦截器
**/
@Configuration
public class MybatisPlusPageConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.DM));//如果配置多个插件,切记分页最后添加
//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
return interceptor;
}
}
①在UserController中接收前端发送 /system/user/list 请求 。
/**
* 分页查询
* @param username
* @param phone
* @param pageNo
* @param pageSize
* @return
*/
@ApiOperation("分页查询")
@GetMapping("/list")
public Result<Map<String,Object>> getUserList(
@RequestParam(value = "username",required = false) String username,
@RequestParam(value = "phone",required = false) String phone,
@RequestParam(value = "pageNo") Long pageNo,
@RequestParam(value = "pageSize") Long pageSize){
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StringUtils.hasLength(username),User::getUsername,username);
// 疑惑:为什么请求参数中phone改成Long就查不出数据,请求参数中的类型是String才能查出数据
wrapper.eq(StringUtils.hasLength(phone),User::getPhone,phone);
wrapper.orderByDesc(User::getId);
Page<User> page = new Page<>(pageNo, pageSize);
userService.page(page,wrapper);
Map<String,Object> data = new HashMap<>();
data.put("total",page.getTotal());
data.put("rows",page.getRecords());
return Result.success(data);
}
②使用Postman/Apifox/Swagger等接口测试工具,获取token,传入参数,进行获取用户列表接口服务测试 。
注意:特别注意,测试工具的使用中,参数传入异常,折磨了两个小时,pageNo、pageSize参数未传入 。
Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'pageNo' for method parameter type Long is not present]
pageNo、pageSize参数正确传入 。
③前端获取数据进行展示 。
①在UserController中注入PasswordEncoder并调用后端服务接口 。
@Resource
private PasswordEncoder passwordEncoder;
// 新增用户
@ApiOperation("添加用户")
@PostMapping
public Result<?> addUser(@RequestBody User user){
user.setPassword(passwordEncoder.encode(user.getPassword()));
userService.addUser(user);
return Result.success("添加成功");
}
②在IUserService中新增用户接口 。
/**
* 新增用户
* @param user
*/
void addUser(User user);
③在UserServiceImpl实现新增用户接口 。
/**
* 新增用户
* @param user
*/
@Override
@Transactional
public void addUser(User user) {
// 写入用户表
this.baseMapper.insert(user);
// 写入用户角色表
}
④使用Postman/Apifox/Swagger等接口测试工具,进行添加用户接口服务测试,出现了一个异常 。
SQL: INSERT INTO XGIS.X_USER ( id, username, password, email, status, phone ) VALUES ( ?, ?, ?, ?, ?, ? )
### Cause: dm.jdbc.driver.DMException: 仅当指定列列表,且SET IDENTITY_INSERT为ON时,才能对自增列赋值
SET IDENTITY_INSERT XGIS.X_USER ON; // 开启自增
SET IDENTITY_INSERT XGIS.X_USER OFF; // 关闭自增
ALTER TABLE XGIS.X_USER DROP IDENTITY; // 删除自增
在UserController控制器获取用户ID的方法 。
/**
* 根据前端传入用户id查询到单条用户信息,用于回显修改弹出框
* @param id
* @return
*/
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable("id") Integer id){
User user = userService.getById(id);
return Result.success(user);
}
在UserController控制器添加修改用户方法 。
/**
* 修改用户
* @param user
* @return
*/
@PutMapping
public Result<?> updateUser(@RequestBody User user){
user.setPassword(null);
userService.updateById(user);
return Result.success("修改成功");
}
使用Postman/Apifox/Swagger等接口测试工具,进行修改用户接口服务测试.
在UserController控制器添加删除用户方法 。
/**
* 根据用户id删除用户
* @param id
* @return
*/
@DeleteMapping("/{id}")
public Result<User> deleteUserById(@PathVariable("id") Integer id){
userService.removeById(id);
return Result.success("删除成功");
}
在application.yml中配置逻辑删除 。
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
logic-delete-field: deleted
logic-not-delete-value: 0
logic-delete-value: 1
mapper-locations: classpath*:mapper/*.xml
type-aliases-package: org.cn
使用Postman/Apifox/Swagger等接口测试工具,进行删除用户接口服务测试.
单表增删改查 。
package org.cn.system.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.cn.common.utils.Result;
import org.cn.system.entity.Role;
import org.cn.system.service.IRoleService;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* 角色表 前端控制器
* </p>
*
* @author LuMin
* @since 2024-11-26
*/
@Api(tags = "角色管理")
@RestController
@RequestMapping("/system/role")
public class RoleController {
@Resource
private IRoleService roleService;
/**
* 角色列表
* @param roleName
* @param roleDesc
* @param pageNo
* @param pageSize
* @return
*/
@ApiOperation("角色列表")
@GetMapping("/list")
public Result<Map<String,Object>> getRoleList(
@RequestParam(value = "roleName",required = false) String roleName,
@RequestParam(value = "roleDesc",required = false) String roleDesc,
@RequestParam(value = "pageNo") Long pageNo,
@RequestParam(value = "pageSize") Long pageSize){
LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StringUtils.hasLength(roleName),Role::getRoleName,roleName);
wrapper.eq(StringUtils.hasLength(roleDesc),Role::getRoleDesc,roleDesc);
wrapper.orderByDesc(Role::getID);
Page<Role> page = new Page<>(pageNo, pageSize);
roleService.page(page,wrapper);
Map<String,Object> data = new HashMap<>();
data.put("total",page.getTotal());
data.put("rows",page.getRecords());
return Result.success(data);
}
/**
* 添加角色
* @param role
* @return
*/
@ApiOperation("添加角色")
@PostMapping("/add")
public Result<?> addRole(@RequestBody Role role){
roleService.save(role);
return Result.success("添加成功");
}
/**
* 根据页面点解获取角色id查询到单条角色信息,用于回显修改弹出框
* @param id
* @return
*/
@ApiOperation("单个角色")
@GetMapping("/{id}")
public Result<Role> getRoleById(@PathVariable("id") Integer id){
Role role = roleService.getById(id);
return Result.success(role);
}
/**
* 修改角色
* @param role
* @return
*/
@ApiOperation("修改角色")
@PutMapping("/update")
public Result<?> updateRole(@RequestBody Role role){
roleService.updateById(role);
return Result.success("修改成功");
}
/**
* 根据角色id删除角色信息
* @param id
* @return
*/
@ApiOperation("删除角色")
@DeleteMapping("/{id}")
public Result<Role> deleteRoleById(@PathVariable("id") Integer id){
roleService.removeById(id);
return Result.success("删除成功");
}
}
Postman工具测试 。
①在Menu实体中加上构建菜单属性 。
@ApiModelProperty("子菜单")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@TableField(exist = false)
private List<Menu> children;
@TableField(exist = false)
@ApiModelProperty("构建meta对象")
private Map<String,Object> meta;
public Map<String, Object> getMeta() {
meta = new HashMap<>();
meta.put("title",title);
meta.put("icon",icon);
return meta;
}
②在MenuController控制器种添加拦截请求 。
注意:将@Controller 改为@RestController,否则会报异常 。
异常:javax.servlet.ServletException: Could not resolve view with name 'system/menu/list' in servlet with name 'dispatcherServlet 。
@Resource
private IMenuService menuService;
@ApiOperation("菜单列表")
@GetMapping("/list")
public Result<List<Menu>> getMenuList () {
List<Menu> menuList = menuService.getMenuList();
return Result.success(menuList);
}
③在IMenuService服务类种构建getMenuList()接口 。
/**
* 菜单列表
* @return
*/
List<Menu> getMenuList();
④在MenuServiceImpl实现类中实现getMenuList()接口 。
@Override
public List<Menu> getMenuList() {
// 一级菜单
LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Menu::getParentId,0);
List<Menu> menuList = this.list(wrapper);
// 填充子菜单
setMenuChildren(menuList);
return menuList;
}
private void setMenuChildren(List<Menu> menuList) {
if (menuList != null) {
for (Menu menu : menuList) {
LambdaQueryWrapper<Menu> subWrapper = new LambdaQueryWrapper<>();
subWrapper.eq(Menu::getParentId,menu.getId());
List<Menu> subMenuList = this.list(subWrapper);
menu.setChildren(subMenuList);
// 递归
setMenuChildren(subMenuList);
}
}
}
⑤在Postman中测试getMenuList()接口 。
①在Role实体中加入菜单属性 。
@ApiModelProperty("菜单列表")
@TableField(exist = false)
private List<Integer> menuIdList;
②在RoleController控制器中改造获取单个角色的方法 。
/**
* 根据角色id查询某个角色所拥有的菜单权限
* @param id
* @return
*/
@ApiOperation("单条角色菜单")
@GetMapping("/{id}")
public Result<Role> getRoleById(@PathVariable("id") Integer id){
Role role = roleService.getRoleById(id);
return Result.success(role);
}
③在IRoleService服务类中构建getRoleById()接口 。
/**
* 根据角色id查询某个角色所拥有的菜单权限
* @param id
* @return
*/
Role getRoleById(Integer id);
④在RoleServiceImpl实现类中实现getRoleById()接口 。
@Override
public Role getRoleById(Integer id) {
Role role = this.baseMapper.selectById(id);
List<Integer> menuIdList = roleMenuMapper.getMenuIdListByRoleId(id);
role.setMenuIdList(menuIdList);
return role;
}
⑤在RoleMenuMapper中构建查询数据库的getMenuIdListByRoleId()接口 。
/**
* 根据角色菜单关联表,关联查询出某个角色拥有多少菜单权限
* @param id
* @return
*/
List<Integer> getMenuIdListByRoleId(Integer id);
⑥在RoleMenuMapper.xml构建自定义查询菜单SQL 。
<select id="getMenuIdListByRoleId" parameterType="Integer" resultType="Integer">
SELECT XRM.MENU_ID
FROM XGIS.X_ROLE_MENU AS XRM,
XGIS.X_MENU AS XM
WHERE XRM.MENU_ID = XM.ID
AND XM.IS_LEAF = 'Y'
AND XRM.ROLE_ID = #{ID}
</select>
⑦在Postman中测试getMenuList()接口 。
①在RoleController控制器中添加addRole()方法 。
/**
* 添加角色菜单
* @param role
* @return
*/
@ApiOperation("添加角色菜单")
@PostMapping("/add")
public Result<?> addRole(@RequestBody Role role){
roleService.addRole(role);
return Result.success("添加成功");
}
②在IRoleService服务类中添加addRole()服务接口 。
/**
* 添加角色菜单
* @param role
*/
void addRole(Role role);
③在IRoleServiceImpl中实现addRole()服务接口 。
注意:使用roleMenuMapper.insert()时,需要在RoleMenu实体中添加构造方法,否则会报错 。
@Override
@Transactional
public void addRole(Role role) {
// 写入角色表
this.baseMapper.insert(role);
// 写入菜单菜单关联表
if (null != role.getMenuIdList()) {
for (Integer menuId : role.getMenuIdList()){
roleMenuMapper.insert(new RoleMenu(null,role.getId(),menuId));
}
}
}
④在ReloMenu实体中添加构造方法 。
public RoleMenu(Object o, Integer roleId, Integer menuId) {
this.roleId = roleId;
this.menuId = menuId;
}
⑦在Postman中测试addRole()接口 。
①在RoleController控制器中添加updateRole()方法 。
/**
* 修改角色菜单:逻辑(根据角色Id把原先角色信息删除掉,再重新新增角色信息即可)
* @param role
* @return
*/
@ApiOperation("修改角色菜单")
@PutMapping("/update")
public Result<?> updateRole(@RequestBody Role role){
roleService.updateRole(role);
return Result.success("修改成功");
}
②在IRoleService服务类中添加 updateRole()服务接口 。
/**
* 修改角色菜单
* @param role
*/
void updateRole(Role role);
③在IRoleServiceImpl中实现addRole()服务接口 。
注意:使用roleMenuMapper.insert()时,需要在RoleMenu实体中添加构造方法,否则会报错 。
@Override
@Transactional
public void updateRole(Role role) {
// 修改角色表
this.baseMapper.updateById(role);
// 删除原有权限
LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RoleMenu::getRoleId,role.getId());
roleMenuMapper.delete(wrapper);
// 新增权限
if (null != role.getMenuIdList()) {
for (Integer menuId : role.getMenuIdList()){
roleMenuMapper.insert(new RoleMenu(null,role.getId(),menuId));
}
}
}
④在ReloMenu实体中添加构造方法 。
public RoleMenu(Object o, Integer roleId, Integer menuId) {
this.roleId = roleId;
this.menuId = menuId;
}
⑦在Postman中测试updateRole()接口 。
①在RoleController控制器中添加deleteRoleById()方法 。
/**
* 删除角色菜单
* @param roleId
* @return
*/
@DeleteMapping("/{roleId}")
public Result<Role> deleteRoleById(@PathVariable("roleId") Integer roleId){
roleService.deleteRoleById(roleId);
return Result.success("删除成功");
}
②在IRoleService服务类中添加 deleteRoleById()服务接口 。
/**
* 删除角色菜单
* @param roleId
*/
void deleteRoleById(Integer roleId);
③在IRoleServiceImpl中实现deleteRoleById()服务接口 。
@Override
@Transactional
public void deleteRoleById(Integer id) {
// 删除角色
this.baseMapper.deleteById(id);
// 删除权限
LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RoleMenu::getRoleId,id);
roleMenuMapper.delete(wrapper);
}
⑦在Postman中测试deleteRoleById()接口 。
①在User实体中加入角色属性 。
/**
* 角色列表
*/
@TableField(exist = false)
private List<Integer> roleIdList;
②在UserController控制器中改造获取单个用户的方法 。
/**
* 根据前端传入用户id查询到单条用户和角色信息,用于回显修改弹出框
* @param id
* @return
*/
@ApiOperation("单条用户角色")
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable("id") Integer id){
User user = userService.getUserById(id);
return Result.success(user);
}
③在IUserService服务类中构建getRoleById()接口 。
/**
* 获取单条用户角色
* @param id
* @return
*/
User getUserById(Integer id);
④在UserServiceImpl实现类中实现getUserById()接口 。
/**
* 单条用户角色
* @param id
* @return
*/
@Override
public User getUserById(Integer id) {
User user = this.baseMapper.selectById(id);
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserRole::getUserId,id);
List<UserRole> userRoleList = userRoleMapper.selectList(wrapper);
List<Integer> roleIdList = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList());
user.setRoleIdList(roleIdList);
return user;
}
⑤在Postman中测试getMenuList()接口 。
①在UserController控制器中添加addUser()方法 。
/**
* 添加用户角色
* @param user
* @return
*/
@ApiOperation("添加用户角色")
@PostMapping("/add")
public Result<?> addUser(@RequestBody User user){
user.setPassword(passwordEncoder.encode(user.getPassword()));
userService.addUser(user);
return Result.success("添加成功");
}
②在IUserService服务类中添加addUser()服务接口 。
/**
* 添加用户角色
* @param user
*/
void addUser(User user);
③在IUsererviceImpl中实现addUser()服务接口 。
注意:使用userRoleMapper.insert()时,需要在UserRole实体中添加构造方法,否则会报错 。
/**
* 添加用户角色
* @param user
*/
@Override
@Transactional
public void addUser(User user) {
// 写入用户表
this.baseMapper.insert(user);
// 写入用户角色表
List<Integer> roleIdList = user.getRoleIdList();
if (roleIdList != null) {
for (Integer roleId : roleIdList){
userRoleMapper.insert(new UserRole(null,user.getId(),roleId));
}
}
}
④在UserRole实体中添加构造方法 。
public UserRole(Object o, Integer id, Integer roleId) {
this.userId = id;
this.roleId = roleId;
}
⑦在Postman中测试addUser()接口 。
①在UserController控制器中添加updateUser()方法 。
/**
* 修改用户角色
* @param user
* @return
*/
@ApiOperation("修改用户角色")
@PutMapping("/update")
public Result<?> updateUser(@RequestBody User user){
user.setPassword(null);
userService.updateUser(user);
return Result.success("修改成功");
}
②在IUserService服务类中添加updateUser()服务接口 。
/**
* 修改用户角色
* @param user
*/
void updateUser(User user);
③在IUsererviceImpl中实现updateUser()服务接口 。
注意:使用userRoleMapper.insert()时,需要在UserRole实体中添加构造方法,否则会报错 。
/**
* 修改用户角色
* @param user
*/
@Override
@Transactional
public void updateUser(User user) {
// 更新用户表
this.baseMapper.updateById(user);
// 删除原有角色
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserRole::getUserId,user.getId());
userRoleMapper.delete(wrapper);
// 设置新的角色
List<Integer> roleIdList = user.getRoleIdList();
if (roleIdList != null) {
for (Integer roleId : roleIdList){
userRoleMapper.insert(new UserRole(null,user.getId(),roleId));
}
}
}
④在UserRole实体中添加构造方法 。
public UserRole(Object o, Integer id, Integer roleId) {
this.userId = id;
this.roleId = roleId;
}
⑦在Postman中测试updateUser()接口 。
①在UserController控制器中添加deleteUserById()方法 。
/**
* 删除用户角色
* @param id
* @return
*/
@ApiOperation("删除用户角色")
@DeleteMapping("/{id}")
public Result<User> deleteUserById(@PathVariable("id") Integer id){
userService.deleteUserById(id);
return Result.success("删除成功");
}
②在IUserService服务类中添加deleteUserById()服务接口 。
/**
* 删除用户角色
* @param id
*/
void deleteUserById(Integer id);
③在IUsererviceImpl中实现deleteUserById()服务接口 。
/**
* 删除用户角色
* @param id
*/
@Override
public void deleteUserById(Integer id) {
// 删除用户
this.baseMapper.deleteById(id);
// 删除角色
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserRole::getUserId,id);
userRoleMapper.delete(wrapper);
}
⑦在Postman中测试deleteById()接口 。
在 MenuMapper.xml 中菜单列表关联查询SQL 。
<select id="getMenuListByUserId" resultType="org.cn.system.entity.Menu">
SELECT DISTINCT XM.*
FROM XGIS.X_USER_ROLE AS XUR,
XGIS.X_ROLE_MENU AS XRM,
XGIS.X_MENU AS XM,
WHERE XM.ID = XRM.MENU_ID
AND XRM.ROLE_ID = XUR.ROLE_ID
AND XUR.USER_ID = #{userId}
AND XM.PARENT_ID = #{parentId}
</select>
在 MenuMapper 中构建获取菜单列表接口:getMenuListByUserId() 。
/**
* 根据前端传入的userId查询到所属角色信息,根据用户角色关联表获取角色ID,
* 再根据角色ID查询所属菜单,并且通过传入参数中parentId是否存在判断是否有子菜单,用于动态路由
* @param userId
* @param parentId
* @return
*/
public List<Menu> getMenuListByUserId(@Param("userId") Integer userId, @Param("parentId") Integer parentId);
在 IMenuService 中构建获取菜单列表服务接口:getMenuListByUserId() 。
/**
* 根据前端传入的userId查询到所属角色信息,根据用户角色关联表获取角色ID,
* 再根据角色ID查询所属菜单,并且通过传入参数中parentId是否存在判断是否有子菜单,用于动态路由
* @param userId
* @return
*/
List<Menu> getMenuListByUserId(Integer userId);
在 MenuServiceImpl 中实现获取菜单列表服务接口:getMenuListByUserId() 。
/**
* 根据前端传入的userId查询到所属角色信息,根据用户角色关联表获取角色ID,
* 再根据角色ID查询所属菜单,并且通过传入参数中parentId是否存在判断是否有子菜单,用于动态路由
* @param userId
* @return
*/
@Override
public List<Menu> getMenuListByUserId(Integer userId) {
// 一级菜单
List<Menu> menuList = this.baseMapper.getMenuListByUserId(userId, 0);
// 子菜单
setMenuListByUserId(userId, menuList);
return menuList;
}
private void setMenuListByUserId(Integer userId, List<Menu> menuList) {
if (menuList != null){
for (Menu menu : menuList) {
List<Menu> subMenuList = this.baseMapper.getMenuListByUserId(userId,menu.getId());
menu.setChildren(subMenuList);
// 递归
setMenuListByUserId(userId,subMenuList);
}
}
}
在 UserMapper.xml 中菜单列表关联查询SQL 。
<select id="getRoleNameByUserId" resultType="java.lang.String">
SELECT XR.ROLE_DESC
FROM XGIS.X_USER_ROLE AS XUR,
XGIS.X_ROLE AS XR
WHERE XUR.ROLE_ID = XR.ID
AND XUR.USER_ID = #{userId}
</select>
在 UserMapper 中构建获取菜单列表接口:getRoleNameByUserId() 。
/**
* 根据用户ID获取角色名称
* @param id
* @return
*/
List<String> getRoleNameByUserId(Integer id);
/**
* 角色列表
* @return
*/
@ApiOperation("角色列表")
@GetMapping("/all")
public Result<List<Role>> getAllRoleList(){
List<Role> roleList = roleService.list();
return Result.success(roleList);
}
在UserController控制器中测试获取当前用户信息接口是否能够获取到用户对应的角色信息、菜单信息 。
调用顺序:getCurrentUserInfo() —>getRoleNameByUserId()—>getMenuListByUserId() 。
/**
* 获取当前用户信息:包括用户所有角色信息,用户所有菜单信息
* @param token
* @return
*/
@ApiOperation("当前用户信息")
@GetMapping("/info")
public Result<Map<String,Object>> getCurrentUserInfo(@RequestParam("token") String token){
// 根据 token 获取用户信息
Map<String,Object> data = userService.getCurrentUserInfo(token);
if (data != null) {
return Result.success(data);
}
return Result.fail(20003,"用户登录无效,请重新登录");
}
最后此篇关于(二)Springboot+vue+达梦数据库构建RBAC权限模型前后端分离脚手架保姆级教程(后端项目)的文章就讲到这里了,如果你想了解更多关于(二)Springboot+vue+达梦数据库构建RBAC权限模型前后端分离脚手架保姆级教程(后端项目)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我读了here您可以使用命令构建模块,因此您无需手动创建一些初始文件。但是这样的命令在 master 上不起作用(在 Odoo 开发分支上): ./oe scaffold Academy ../my-
我是 Grails 新手。我有一个 Person 域类: class Person { String firstName String lastName String gend
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 9 年前。 Improve this
我使用 yeoman 作为脚手架工具,但出现以下错误。任何人都可以帮我解决这个问题。我正在使用 Windows 8 环境。 karma-jasmine@0.1.5 node_modules\karma
我正在查看使用grails generate-all生成的 Controller 。为什么同时存在create和save操作,是否有原因?对于我来说,仅将create作为操作并将save作为服务对我来
我是个新手。我现在正在研究脚手架模板,尤其是在 Controller 上。我每次生成时都想要一个自定义的 Controller ,所以我使用了“安装模板”。我总是在 Controller 上创建Com
我有一个类库,其中包含一些模型类和一个 DbContext 类(所有这些类都是公共(public)的)。该类库由 MVC-5 应用程序引用。 是否可以使用该引用类库中的模型类来构建该 MVC-5 应用
我有一个使用 Twitter Bootstrap 构建的单页应用程序。我的应用程序有几个模态对话框,它们通常占页面宽度的 70%,并且水平居中。我的页面和模态设置是这样的: 我在模态框内放置了一行和几
我正在开发一个具有某种“协调器”界面的网络应用程序。我希望能够支持我正在使用的 Eyefinity 设置(即 3 个纵向显示器,分辨率为 3600x1920,或 3780x1920,并启用了边框校正)
在处理 codeigniter 中的脚手架时,我有哪些选择 - 人们经常使用脚手架吗?或者是否有更好的方法来快速生成用于 CRUD 应用程序的代码? 我已经安装了 Spark 塞:http://cod
我正在我工作的公司解决方案中实现存储库模式,将后端项目中的模型类与 DbContexts 项目中的数据库上下文和迁移分开。 我正在使用 Scaffold-DbContext 将我的后端项目设置为模型类
目录 1、前言 2、什么是脚手架呢? 3、谁提供了脚手架? 1、Spring 官网脚手架 2、阿里云脚手架 4、手撸一个脚手架!
我在脚手架中有一个应用栏。 return Scaffold( appBar: styling.appBar( AppBar( leading: styling.icon
我正在尝试使用 Compose 设计一个布局,其中包括: 热门应用栏 正文(内容) 底部应用栏 单击时表示菜单的底部表(模态底部表) -------TopAppBar------ ------主要内容
有谁知道如何为所有对象属性“构建”或“生成”构造函数块?我有 VS2010 和 Resharper,我希望生成类似的东西: public Customer CustomerB = new Cu
我正在尝试在 grails 中上传文件。生成的脚手架对我不起作用,给我一个 404 寻找“save.jsp” 域名 class Simple{ byte [] avatar static co
因为我已经手动调整了我的实体和 Controller 以及 View ,所以我希望 roo 不更改它们中的任何一个。尽管如此,我还是希望继续为所有新实体搭建脚手架。我怎么能那样做 最佳答案 您可以通过
我使用命令grails generate-restful-controller Domain创建了一个 Restful Controller ,如何在创建的 Controller 中设置scaffol
我有一个定义了 staticscaffold = true 的 Controller ,以及一些自定义操作。 我想确保只有登录用户和 ADMIN 类型的用户(我们域中的某些 Enum 值)才能访问它。
我有一个表,其中某些列的值相对较长,并且在我的 Grails 应用程序中为其打开了动态脚手架(我使用的是 Grails 3.3.8)。因此,在某些屏幕分辨率下,它们不适合屏幕,并且最右边的列最终会出现
我是一名优秀的程序员,十分优秀!