SpringBoot整合SpringSecurity

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: SpringBoot整合SpringSecurity

主页:写程序的小王叔叔的博客欢迎来访👀

支持:点赞收藏关注


1、说明

SpringSecurity是一个用于Java 企业级应用程序的安全框架,主要包含用户认证和用户授权两个方面.相比较Shiro而言,Security功能更加的强大,它可以很容易地扩展以满足更多安全控制方面的需求,但也相对它的学习成本会更高,两种框架各有利弊.实际开发中还是要根据业务和项目的需求来决定使用哪一种.

JWT是在Web应用中安全传递信息的规范,从本质上来说是Token的演变,是一种生成加密用户身份信息的Token,特别适用于分布式单点登陆的场景,无需在服务端保存用户的认证信息,而是直接对Token进行校验获取用户信息,使单点登录更为简单灵活.

2、项目环境

SpringBoot版本:2.1.6

SpringSecurity版本: 5.1.5

MyBatis-Plus版本: 3.1.0

JDK版本:1.8

数据表(SQL文件在项目中): 数据库中测试号的密码进行了加密,密码皆为123456:


Maven依赖如下:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--Security依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- MybatisPlus 核心库 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.1.0</version></dependency><!-- 引入阿里数据库连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.6</version></dependency><!-- StringUtilS工具 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.5</version></dependency><!-- JSON工具 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.45</version></dependency><!-- JWT依赖 --><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId><version>1.0.9.RELEASE</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency></dependencies>

配置如下:

# 配置端口server:  port: 8764spring:# 配置数据源  datasource:    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/sans_security?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
    username: root
    password: 123456    type: com.alibaba.druid.pool.DruidDataSource
# JWT配置jwt:# 密匙KEY  secret: JWTSecret
# HeaderKEY  tokenHeader: Authorization
# Token前缀字符  tokenPrefix: Sans-
# 过期时间 单位秒 1天后过期=86400 7天后过期=604800  expiration: 86400# 配置不需要认证的接口  antMatchers: /index/**,/login/**,/favicon.ico
# Mybatis-plus相关配置mybatis-plus:# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)  mapper-locations: classpath:mapper/*.xml
# 以下配置均有默认值,可以不设置  global-config:    db-config:#主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";      id-type: AUTO
#字段策略 IGNORED:"忽略判断" NOT_NULL:"非 NULL 判断") NOT_EMPTY:"非空判断"      field-strategy: NOT_EMPTY
#数据库类型      db-type: MYSQL
  configuration:# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射    map-underscore-to-camel-case: true# 返回map时true:当查询数据为空时字段返回为null,false:不加这个查询数据为空时,字段将被隐藏    call-setters-on-nulls: true# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3、编写项目基础类

Entity,Dao,Service,及等SpringSecurity用户的Entity,Service类等在这里省略,请参考源码

编写JWT工具类

/*** JWT工具类*/@Slf4jpublicclassJWTTokenUtil {
publicstaticStringcreateAccessToken(SelfUserEntityselfUserEntity){
// 登陆成功生成JWTStringtoken=Jwts.builder()
// 放入用户名和用户ID                .setId(selfUserEntity.getUserId()+"")
// 主题                .setSubject(selfUserEntity.getUsername())
// 签发时间                .setIssuedAt(newDate())
// 签发者                .setIssuer("sans")
// 自定义属性 放入用户拥有权限                .claim("authorities", JSON.toJSONString(selfUserEntity.getAuthorities()))
// 失效时间                .setExpiration(newDate(System.currentTimeMillis() +JWTConfig.expiration))
// 签名算法和密钥                .signWith(SignatureAlgorithm.HS512, JWTConfig.secret)
                .compact();
returntoken;
    }
}

编写暂无权限处理类

@ComponentpublicclassUserAuthAccessDeniedHandlerimplementsAccessDeniedHandler{
@Overridepublicvoidhandle(HttpServletRequestrequest, HttpServletResponseresponse, AccessDeniedExceptionexception){
ResultUtil.responseJson(response,ResultUtil.resultCode(403,"未授权"));
    }
}

编写登录失败处理类

@Slf4j@ComponentpublicclassUserLoginFailureHandlerimplementsAuthenticationFailureHandler {
@OverridepublicvoidonAuthenticationFailure(HttpServletRequestrequest, HttpServletResponseresponse, AuthenticationExceptionexception){
// 这些对于操作的处理类可以根据不同异常进行不同处理if (exceptioninstanceofUsernameNotFoundException){
log.info("【登录失败】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用户名不存在"));
        }
if (exceptioninstanceofLockedException){
log.info("【登录失败】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用户被冻结"));
        }
if (exceptioninstanceofBadCredentialsException){
log.info("【登录失败】"+exception.getMessage());
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"用户名密码不正确"));
        }
ResultUtil.responseJson(response,ResultUtil.resultCode(500,"登录失败"));
    }
}

编写登录成功处理类

@Slf4j@ComponentpublicclassUserLoginSuccessHandlerimplementsAuthenticationSuccessHandler {
@OverridepublicvoidonAuthenticationSuccess(HttpServletRequestrequest, HttpServletResponseresponse, Authenticationauthentication){
// 组装JWTSelfUserEntityselfUserEntity= (SelfUserEntity) authentication.getPrincipal();
Stringtoken=JWTTokenUtil.createAccessToken(selfUserEntity);
token=JWTConfig.tokenPrefix+token;
// 封装返回参数Map<String,Object>resultData=newHashMap<>();
resultData.put("code","200");
resultData.put("msg", "登录成功");
resultData.put("token",token);
ResultUtil.responseJson(response,resultData);
    }
}

编写登出成功处理类

@ComponentpublicclassUserLogoutSuccessHandlerimplementsLogoutSuccessHandler {
@OverridepublicvoidonLogoutSuccess(HttpServletRequestrequest, HttpServletResponseresponse, Authenticationauthentication){
Map<String,Object>resultData=newHashMap<>();
resultData.put("code","200");
resultData.put("msg", "登出成功");
SecurityContextHolder.clearContext();
ResultUtil.responseJson(response,ResultUtil.resultSuccess(resultData));
    }
}

4、编写Security核心类

编写自定义登录验证类

@ComponentpublicclassUserAuthenticationProviderimplementsAuthenticationProvider {
@AutowiredprivateSelfUserDetailsServiceselfUserDetailsService;
@AutowiredprivateSysUserServicesysUserService;
@OverridepublicAuthenticationauthenticate(Authenticationauthentication) throwsAuthenticationException {
// 获取表单输入中返回的用户名StringuserName= (String) authentication.getPrincipal();
// 获取表单中输入的密码Stringpassword= (String) authentication.getCredentials();
// 查询用户是否存在SelfUserEntityuserInfo=selfUserDetailsService.loadUserByUsername(userName);
if (userInfo==null) {
thrownewUsernameNotFoundException("用户名不存在");
        }
// 我们还要判断密码是否正确,这里我们的密码使用BCryptPasswordEncoder进行加密的if (!newBCryptPasswordEncoder().matches(password, userInfo.getPassword())) {
thrownewBadCredentialsException("密码不正确");
        }
// 还可以加一些其他信息的判断,比如用户账号已停用等判断if (userInfo.getStatus().equals("PROHIBIT")){
thrownewLockedException("该用户已被冻结");
        }
// 角色集合Set<GrantedAuthority>authorities=newHashSet<>();
// 查询用户角色List<SysRoleEntity>sysRoleEntityList=sysUserService.selectSysRoleByUserId(userInfo.getUserId());
for (SysRoleEntitysysRoleEntity: sysRoleEntityList){
authorities.add(newSimpleGrantedAuthority("ROLE_"+sysRoleEntity.getRoleName()));
        }
userInfo.setAuthorities(authorities);
// 进行登录returnnewUsernamePasswordAuthenticationToken(userInfo, password, authorities);
    }
@Overridepublicbooleansupports(Class<?>authentication) {
returntrue;
    }
}

编写自定义PermissionEvaluator注解验证

@ComponentpublicclassUserPermissionEvaluatorimplementsPermissionEvaluator {
@AutowiredprivateSysUserServicesysUserService;
@OverridepublicbooleanhasPermission(Authenticationauthentication, ObjecttargetUrl, Objectpermission) {
// 获取用户信息SelfUserEntityselfUserEntity=(SelfUserEntity) authentication.getPrincipal();
// 查询用户权限(这里可以将权限放入缓存中提升效率)Set<String>permissions=newHashSet<>();
List<SysMenuEntity>sysMenuEntityList=sysUserService.selectSysMenuByUserId(selfUserEntity.getUserId());
for (SysMenuEntitysysMenuEntity:sysMenuEntityList) {
permissions.add(sysMenuEntity.getPermission());
        }
// 权限对比if (permissions.contains(permission.toString())){
returntrue;
        }
returnfalse;
    }
@OverridepublicbooleanhasPermission(Authenticationauthentication, SerializabletargetId, StringtargetType, Objectpermission) {
returnfalse;
    }
}

编写SpringSecurity核心配置类

@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled=true) //开启权限注解,默认是关闭的publicclassSecurityConfigextendsWebSecurityConfigurerAdapter {
/*** 自定义登录成功处理器*/@AutowiredprivateUserLoginSuccessHandleruserLoginSuccessHandler;
/*** 自定义登录失败处理器*/@AutowiredprivateUserLoginFailureHandleruserLoginFailureHandler;
/*** 自定义注销成功处理器*/@AutowiredprivateUserLogoutSuccessHandleruserLogoutSuccessHandler;
/*** 自定义暂无权限处理器*/@AutowiredprivateUserAuthAccessDeniedHandleruserAuthAccessDeniedHandler;
/*** 自定义未登录的处理器*/@AutowiredprivateUserAuthenticationEntryPointHandleruserAuthenticationEntryPointHandler;
/*** 自定义登录逻辑验证器*/@AutowiredprivateUserAuthenticationProvideruserAuthenticationProvider;
@BeanpublicBCryptPasswordEncoderbCryptPasswordEncoder(){
returnnewBCryptPasswordEncoder();
    }
/*** 注入自定义PermissionEvaluator*/@BeanpublicDefaultWebSecurityExpressionHandleruserSecurityExpressionHandler(){
DefaultWebSecurityExpressionHandlerhandler=newDefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(newUserPermissionEvaluator());
returnhandler;
    }
/*** 配置登录验证逻辑*/@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth){
//这里可启用我们自己的登陆验证逻辑auth.authenticationProvider(userAuthenticationProvider);
    }
@Overrideprotectedvoidconfigure(HttpSecurityhttp) throwsException {
http.authorizeRequests()
//不进行权限验证的请求或资源(从配置文件中读取)               .antMatchers(JWTConfig.antMatchers.split(",")).permitAll()
//其他的需要登陆后才能访问                .anyRequest().authenticated()
                .and()
//配置未登录自定义处理类                .httpBasic().authenticationEntryPoint(userAuthenticationEntryPointHandler)
                .and()
//配置登录地址                .formLogin()
                .loginProcessingUrl("/login/userLogin")
//配置登录成功自定义处理类                .successHandler(userLoginSuccessHandler)
//配置登录失败自定义处理类                .failureHandler(userLoginFailureHandler)
                .and()
//配置登出地址                .logout()
                .logoutUrl("/login/userLogout")
//配置用户登出自定义处理类                .logoutSuccessHandler(userLogoutSuccessHandler)
                .and()
//配置没有权限自定义处理类                .exceptionHandling().accessDeniedHandler(userAuthAccessDeniedHandler)
                .and()
// 开启跨域                .cors()
                .and()
// 取消跨站请求伪造防护                .csrf().disable();
// 基于Token不需要sessionhttp.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 禁用缓存http.headers().cacheControl();
// 添加JWT过滤器http.addFilter(newJWTAuthenticationTokenFilter(authenticationManager()));
    }
}

5、编写JWT拦截类

编写JWT接口请求校验拦截器

/*** JWT接口请求校验拦截器* 请求接口时会进入这里验证Token是否合法和过期* @Author Sans* @CreateTime 2019/10/5 16:41*/@Slf4jpublicclassJWTAuthenticationTokenFilterextendsBasicAuthenticationFilter {
publicJWTAuthenticationTokenFilter(AuthenticationManagerauthenticationManager) {
super(authenticationManager);
    }
@OverrideprotectedvoiddoFilterInternal(HttpServletRequestrequest, HttpServletResponseresponse, FilterChainfilterChain) throwsServletException, IOException {
// 获取请求头中JWT的TokenStringtokenHeader=request.getHeader(JWTConfig.tokenHeader);
if (null!=tokenHeader&&tokenHeader.startsWith(JWTConfig.tokenPrefix)) {
try {
// 截取JWT前缀Stringtoken=tokenHeader.replace(JWTConfig.tokenPrefix, "");
// 解析JWTClaimsclaims=Jwts.parser()
                        .setSigningKey(JWTConfig.secret)
                        .parseClaimsJws(token)
                        .getBody();
// 获取用户名Stringusername=claims.getSubject();
StringuserId=claims.getId();
if(!StringUtils.isEmpty(username)&&!StringUtils.isEmpty(userId)) {
// 获取角色List<GrantedAuthority>authorities=newArrayList<>();
Stringauthority=claims.get("authorities").toString();
if(!StringUtils.isEmpty(authority)){
List<Map<String,String>>authorityMap=JSONObject.parseObject(authority, List.class);
for(Map<String,String>role : authorityMap){
if(!StringUtils.isEmpty(role)) {
authorities.add(newSimpleGrantedAuthority(role.get("authority")));
                            }
                        }
                    }
//组装参数SelfUserEntityselfUserEntity=newSelfUserEntity();
selfUserEntity.setUsername(claims.getSubject());
selfUserEntity.setUserId(Long.parseLong(claims.getId()));
selfUserEntity.setAuthorities(authorities);
UsernamePasswordAuthenticationTokenauthentication=newUsernamePasswordAuthenticationToken(selfUserEntity, userId, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            } catch (ExpiredJwtExceptione){
log.info("Token过期");
            } catch (Exceptione) {
log.info("Token无效");
            }
        }
filterChain.doFilter(request, response);
return;
    }
}

6、权限注解和hasPermission权限扩展

Security允许我们在定义URL方法访问所应有的注解权限时使用SpringEL表达式,在定义所需的访问权限时如果对应的表达式返回结果为true则表示拥有对应的权限,反之则没有权限,会进入到我们配置的UserAuthAccessDeniedHandler(暂无权限处理类)中进行处理.这里举一些例子,代码中注释有对应的描述。


表达式

@PreAuthorize("hasRole('ADMIN')")
@RequestMapping(value="/info",method=RequestMethod.GET)
publicMap<String,Object>userLogin(){
Map<String,Object>result=newHashMap<>();
SelfUserEntityuserDetails=SecurityUtil.getUserInfo();
result.put("title","管理端信息");
result.put("data",userDetails);
returnResultUtil.resultSuccess(result);
    }
@PreAuthorize("hasAnyRole('ADMIN','USER')")
@RequestMapping(value="/list",method=RequestMethod.GET)
publicMap<String,Object>list(){
Map<String,Object>result=newHashMap<>();
List<SysUserEntity>sysUserEntityList=sysUserService.list();
result.put("title","拥有用户或者管理员角色都可以查看");
result.put("data",sysUserEntityList);
returnResultUtil.resultSuccess(result);
    }
@PreAuthorize("hasRole('ADMIN') and hasRole('USER')")
@RequestMapping(value="/menuList",method=RequestMethod.GET)
publicMap<String,Object>menuList(){
Map<String,Object>result=newHashMap<>();
List<SysMenuEntity>sysMenuEntityList=sysMenuService.list();
result.put("title","拥有用户和管理员角色都可以查看");
result.put("data",sysMenuEntityList);
returnResultUtil.resultSuccess(result);
    }

通常情况下使用hasRole和hasAnyRole基本可以满足大部分鉴权需求,但是有时候面对更复杂的场景上述常规表示式无法完成权限认证,Security也为我们提供了解决方案.通过hasPermission()来扩展表达式.使用hasPermission()首先要实现PermissionEvaluator接口


@ComponentpublicclassUserPermissionEvaluatorimplementsPermissionEvaluator {
@AutowiredprivateSysUserServicesysUserService;
@OverridepublicbooleanhasPermission(Authenticationauthentication, ObjecttargetUrl, Objectpermission) {
// 获取用户信息SelfUserEntityselfUserEntity=(SelfUserEntity) authentication.getPrincipal();
// 查询用户权限(这里可以将权限放入缓存中提升效率)Set<String>permissions=newHashSet<>();
List<SysMenuEntity>sysMenuEntityList=sysUserService.selectSysMenuByUserId(selfUserEntity.getUserId());
for (SysMenuEntitysysMenuEntity:sysMenuEntityList) {
permissions.add(sysMenuEntity.getPermission());
        }
// 权限对比if (permissions.contains(permission.toString())){
returntrue;
        }
returnfalse;
    }
@OverridepublicbooleanhasPermission(Authenticationauthentication, SerializabletargetId, StringtargetType, Objectpermission) {
returnfalse;
    }
}

在请求方法上添加hasPermission示例

@PreAuthorize("hasPermission('/admin/userList','sys:user:info')")
@RequestMapping(value="/userList",method=RequestMethod.GET)
publicMap<String,Object>userList(){
Map<String,Object>result=newHashMap<>();
List<SysUserEntity>sysUserEntityList=sysUserService.list();
result.put("title","拥有sys:user:info权限都可以查看");
result.put("data",sysUserEntityList);
returnResultUtil.resultSuccess(result);
    }

hasPermission可以也可以和其他表达式联合使用

@PreAuthorize("hasRole('ADMIN') and hasPermission('/admin/adminRoleList','sys:role:info')")
@RequestMapping(value="/adminRoleList",method=RequestMethod.GET)
publicMap<String,Object>adminRoleList(){
Map<String,Object>result=newHashMap<>();
List<SysRoleEntity>sysRoleEntityList=sysRoleService.list();
result.put("title","拥有ADMIN角色和sys:role:info权限可以访问");
result.put("data",sysRoleEntityList);
returnResultUtil.resultSuccess(result);
    }

7、测试

创建账户这里用户加密使用了Security推荐的bCryptPasswordEncoder方法

/*** 注册用户*/@TestpublicvoidcontextLoads() {
// 注册用户SysUserEntitysysUserEntity=newSysUserEntity();
sysUserEntity.setUsername("sans");
sysUserEntity.setPassword(bCryptPasswordEncoder.encode("123456"));
// 设置用户状态sysUserEntity.setStatus("NORMAL");
sysUserService.save(sysUserEntity);
// 分配角色 1:ADMIN 2:USERSysUserRoleEntitysysUserRoleEntity=newSysUserRoleEntity();
sysUserRoleEntity.setRoleId(2L);
sysUserRoleEntity.setUserId(sysUserEntity.getUserId());
sysUserRoleService.save(sysUserRoleEntity);
    }

登录USER角色账号,登录成功后我们会获取到身份认证的Token

访问USER角色的接口,把上一步获取到的Token设置在Headers中,Key为Authorization,我们之前实现的JWTAuthenticationTokenFilter拦截器会根据请求头中的Authorization获取并解析Token使用USER角色Token访问ADMIN角色的接口,会被拒绝,告知未授权(暂无权限会进入我们定义的UserAuthAccessDeniedHandler这个类进行处理)。更换ADMIN角色进行登录并访问ADMIN接口



⚠️注意 ~

💯本期内容就结束了,如果内容有误,麻烦大家评论区指出!

如有疑问❓可以在评论区💬或私信💬,尽我最大能力🏃‍♀️帮大家解决👨‍🏫!

如果我的文章有帮助到您,欢迎点赞+关注✔️鼓励博主🏃,您的鼓励是我分享的动力🏃🏃🏃~

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
安全 Java 关系型数据库
springboot整合springsecurity,从数据库中认证
本文介绍了如何在SpringBoot应用中整合Spring Security,并从数据库中进行用户认证的完整步骤,包括依赖配置、数据库表创建、用户实体和仓库接口、用户详情服务类、安全配置类、控制器类以及数据库初始化器的实现。
108 3
springboot整合springsecurity,从数据库中认证
|
1月前
|
NoSQL Java Redis
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
这篇文章介绍了Redis的基本命令,并展示了如何使用Netty框架直接与Redis服务器进行通信,包括设置Netty客户端、编写处理程序以及初始化Channel的完整示例代码。
48 1
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
|
1月前
|
Java 测试技术 开发者
springboot学习四:Spring Boot profile多环境配置、devtools热部署
这篇文章主要介绍了如何在Spring Boot中进行多环境配置以及如何整合DevTools实现热部署,以提高开发效率。
62 2
|
1月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
102 1
|
1月前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
26 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
1月前
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
26 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
|
1月前
|
Java 测试技术 Spring
springboot学习三:Spring Boot 配置文件语法、静态工具类读取配置文件、静态工具类读取配置文件
这篇文章介绍了Spring Boot中配置文件的语法、如何读取配置文件以及如何通过静态工具类读取配置文件。
54 0
springboot学习三:Spring Boot 配置文件语法、静态工具类读取配置文件、静态工具类读取配置文件
|
1月前
|
SQL Java 数据库
Springboot+spring-boot-starter-data-jdbc实现数据库的操作
本文介绍了如何使用Spring Boot的spring-boot-starter-data-jdbc依赖来操作数据库,包括添加依赖、配置数据库信息和编写基于JdbcTemplate的数据访问代码。
62 2
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
180 2
|
2月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)

热门文章

最新文章

下一篇
无影云桌面