SpringSecurity认证授权及项目集成

本文涉及的产品
多模态交互后付费免费试用,全链路、全Agent
简介: 本文介绍了基于Spring Security的权限管理框架,涵盖认证、授权与鉴权核心概念,通过快速入门示例演示集成流程,并结合数据库实现用户认证。进一步扩展实现正常登录,JWT登录及鉴权管理器,实现灵活的安全控制,适用于前后端分离项目中的权限设计与实践。

一、权限框架

1. 概述

权限管理是所有后台系统的都会涉及的一个重要组成部分,主要目的是对不同的人访问资源进行权限的控制,避免因权限控制缺失或操作不当引发的风险问题,如操作错误,隐私数据泄露等问题。那么如何在项目中实现权限呢?

使用权限框架Spring Security

市面上除了Spring Security,还有其他成熟的安全框架,比如Apache Shiro,但是在养老项目目前已经在使用Spring框架,那么Spring Security就是更好的选择。并且在使用市场占比上Spring Security也远超于Shiro

2. 核心概念

  • 认证

大家可以简单的理解为:用户登录的行为就是认证(你是谁)

判断用户是否存在,判断用户密码是否正确

  • 授权

授权就是在后管配置用户所在的角色是否有权限访问某些资源。

我们有很多的资源api列表,那这个登录后的用户所在的角色是否拥有这个api访问权限

  • 鉴权

用户通过认证并被授权后进行的一道安全检查,判断用户是否有权利执行或访问该资源(校验资源)

image.png

二. 快速入门

1、创建一个springboot项目

2、在pom文件引入依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

3、启动项目,到浏览器输入访问路径会出现以下页面

image.png

默认用户名是user,密码会输出在控制台,相当于目前在内存中写死了用户名和密码

image.png

输入用户名user(默认值)和密码后,会再次重定向访问到/hello的接口,spring security底层默认是使用session来维护登录态的,可以通过捉包观察请求头中携带的cookie信息

结论:

只要是集成了Spring Security框架之后,项目中的所有资源都得到了保护,必须经过认证之后才能访问,为什么只是引入了pom依赖之后就能保护程序呢?关键还是框架内容底层利用springboot自动配置原理,注入了一系列的过滤器、接口来实现的。

这里就完成了Spring Security的入门程序,其实Spring-Security其内部基础的处理方式就是通过过滤器来实现的,来我们看下刚才的例子用到的一些过滤器

image.png

下面是一些spring security过滤器,知道有就行,不需要记

过滤器

作用

WebAsyncManagerlntegrationFilter

将WebAsyncManger与SpringSecurity上下文进行集成

SecurityContextPersistenceFilter

在处理请求之前,将安全信息加载到SecurityContextHolder中

HeaderWriterFilter

处理头信息假如响应中

CsrfFilter

处理CSRF攻击

LogoutFilter

处理注销登录

UsernamePasswordAuthenticationFilter

处理表单登录

DefaultLoginPageGeneratingFilter

配置默认登录页面

DefaultLogoutPageGeneratingFiltel

配置默认注销页面

BasicAuthenticationFilter

外理HttpBasic登录

RequestCacheAwareFilter

处理请求缓存

SecurityContextHolderAwareRequestFilter

包装原始请求

AnonvmousAuthenticationFilter

配置匿名认证

SessionManagementFilter

外理session并发问题

ExceptionTranslationFilter

处理认证/授权中的异常


我们刚才的实现都是基于内存来构建用户的,在实际开发中,用户肯定会保存到数据库中,在Spring Security框架中提供了一个UserDetailsService接口,它的主要作用是提供用户详细信息。具体来说,当用户尝试进行身份验证时,UserDetailsService会被调用,以获取与用户相关的详细信息。这些详细信息包括用户的用户名、密码、角色等


三、项目集成案例:


上面基本都是理论知识,下面我们来看看通过数据库查询用户信息,进行自定义认证

1、执行流程图

image.png



新创建一个UserDetailsServiceImpl,让它实现UserDetailsService ,代码如下

1、

package com.itheima.project.config;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询数据库中的用户,并且返回框架要求的UserDetails 
        return null;
    }
}
  • 当前对象需要让spring容器管理,所以在类上添加注解@Component
  • 大家注意一下loadUserByUsername方法的返回值,叫做UserDetails,它有一个实现类也叫做User,如果正常返回这个对象且不为null,代表用户存在,如果抛出异常或者返回null,代表用户不存在,就不会进行后续校验密码的流程了
2. 数据库查询用户

我们下面就来实现数据库去查询用户,我们可以直接使用我们项目中的用户表,实现的步骤如下:

  • 导入相关依赖(数据库、mybaits等)
  • 添加配置:连接数据库、mybatis配置等(application.yml)
  • 编写实体类和mapper
  • 改造UserDetailsServiceImpl(用户从数据库中获取)
  1. pom文件添加依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.1</version>
</dependency>
<!--MySQL支持-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>
3、application.yml添加数据库相关配置
#服务配置
server:
  #端口
  port: 8080
spring:
  application:
    name: springsecurity-demo
  #数据源配置
  datasource:
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://192.168.200.146:3306/security_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
      username: root
      password: heima123
# MyBatis配置
mybatis:
  #mapper配置文件,classpath不带*只会扫描当前工程项目,带*除了会扫描本项目工程,还会扫描第三方依赖
  mapper-locations: classpath*:mapper*/*Mapper.xml
  type-aliases-package: com.itheima.project.entity
  configuration:
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 驼峰下划线转换
    map-underscore-to-camel-case: true
    use-generated-keys: true
    default-statement-timeout: 60
    default-fetch-size: 100
4、表结构及实体类和mapper

在中州养老虚拟机的数据库中新创建一个数据库,名字为:security_db

导入课程资料的权限基础代码中的sql文件夹的sys_user.sql脚本

用户实体类

package com.itheima.project.entity;
import lombok.Data;
@Data
public class User {
    public Long id;
    /**
     * 用户账号
     */
    private String username;
    /**
     * 密码
     */
    private String password;
    /**
     * 用户昵称
     */
    private String nickName;
}

创建UserMapper接口,我们只需要定义一个根据用户名查询的方法即可


package com.itheima.project.mapper;
import com.itheima.project.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
/**
 * @author sjqn
 */
 //注意这个@Mapper注解必加,因为本项目没有UserMapper.xml,也没有在启动类指定@MapperScan扫描路径,默认springboot启动会在启动类所在的包以及子包扫描@Mapper注解
@Mapper
public interface UserMapper {
    @Select("select * from sys_user where username = #{username}")
    public User findByUsername(String username);
    
}
5、创建UserDetailsService的实现类UserDetailsServiceImpl
package com.itheima.project.config;
import cn.hutool.core.util.ObjectUtil;
import com.itheima.project.entity.User;
import com.itheima.project.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
 * @author sjqn
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询用户
        User user = userMapper.findByUsername(username);
        if(ObjectUtil.isEmpty(user)){
            throw new RuntimeException("用户不存在");
        }
        SimpleGrantedAuthority user_role = new SimpleGrantedAuthority("user");
        SimpleGrantedAuthority admin_role = new SimpleGrantedAuthority("admin");
        List<GrantedAuthority> list = new ArrayList<>();
        list.add(user_role);
        list.add(admin_role);
        return new org.springframework.security.core.userdetails.User(user.getUsername()
                ,user.getPassword()
                , list);
    }
}
6、测试

重启项目之后,可以根据数据库中有的用户进行登录,如果登录成功则表示整合成功


自定义UserDetails

上述代码中,返回的UserDetails或者是User都是框架提供的类,我们在项目开发的过程中,很多需求都是我们自定义的属性,我们需要扩展该怎么办?

其实,我们可以自定义一个类,来实现UserDetails,在自己定义的类中,就可以扩展自己想要的内容,如下代码:

package com.itheima.project.entity;
import cn.hutool.core.util.ObjectUtil;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@Data
public class UserAuth implements UserDetails {
    private String username;
    private String password;
    private String nickName;
    private List<String> roleList;
    
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if(ObjectUtil.isEmpty(roleList)){
            return null;
        }
        return roleList.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role)).collect(Collectors.toList());
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}

然后,我们可以继续改造UserDetailsServiceImpl中检验用户的逻辑,代码如下:

package com.itheima.project.config;
import cn.hutool.core.util.ObjectUtil;
import com.itheima.project.entity.User;
import com.itheima.project.entity.UserAuth;
import com.itheima.project.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
 * @author sjqn
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询用户
        User user = userMapper.findByUsername(username);
        if(ObjectUtil.isEmpty(user)){
            throw new RuntimeException("用户不存在");
        }
        UserAuth userAuth = new UserAuth();
        userAuth.setUsername(user.getUsername());
        userAuth.setPassword(user.getPassword());
        userAuth.setNickName(user.getNickName());
        //添加角色
        List<String> roleList=new ArrayList<>();
        //加入判断,方便后面授权的时候使用
        if(ObjectUtil.equals("user@qq.com",username)){
            roleList.add("USER");
        }
        if(ObjectUtil.equals("admin@qq.com",username)){
            roleList.add("USER");
            roleList.add("ADMIN");
        }
        userAuth.setRoleList(roleList);
        return userAuth;
    }
}

根据执行流程,loadUserByUsername方法返回的UserDetails对象也会最终被包装到Authentication对象中的principal属性中一直往后传递,最后整个Authentication对象也会被放进去ThreadLocal中,所以接下里我们修改HelloController的hello方法,使用getPrincipal()方法读取认证自定义对象。

@RequestMapping("/hello")
public String hello(){
    //认证成功,可以得到用户信息
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    String name = authentication.getName();
    //获取自定义属性
    UserAuth principal = (UserAuth) authentication.getPrincipal();
    return "hello "+name+" 昵称 "+principal.getNickName();
}


测试

重启项目之后,可以根据数据库中有的用户进行登录,并且能够正常显示用户昵称数据表示成功

1、授权和鉴权


授权的方式包括web授权和方法授权

  • web授权是通过url拦截进行授权,基本用这种
  • 方法授权是通过方法拦截进行授权,通过注解的方式控制权限,粒度小,耦合度高


2、web授权


image.png

下面让我们进行一个案例学习:
  1. 准备工作,修改HelloController,增加两个方法,主要是为了方便后边进行测试
@RequestMapping("/hello/user")
public String helloUser(){
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    String name = authentication.getName();
    return "hello-user "+name;
}
@RequestMapping("/hello/admin")
public String helloAdmin(){
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    String name = authentication.getName();
    return "hello-admin "+name;
}
  1. 新建一个 SecurityConfig配置类,实现方法,添加对以上两个地址的角色控制
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.formLogin()             //自定义自己编写的登陆页面
        .loginPage("/login.html")    //登录页面设置
        .loginProcessingUrl("/login") //登录访问路径
        .permitAll()//登录页和登录访问路径无需登录也可以访问
        .and()
        .authorizeRequests()
        .antMatchers("/css/**","/images/**").permitAll()
        .antMatchers("/hello/user").hasRole("USER")
        .antMatchers("/hello/admin").hasAnyRole("ADMIN")//hasAnyRole支持批量对某个url增加多个角色
        .anyRequest().authenticated()
        .and()
        .csrf().disable();    //关闭csrf防护
    return http.build();
}

目前这种资源和角色关系是写死在配置类中的,后续我们要改成从数据库中根据RBAC模型设计的表进行关联查询

  1. 测试

分别使用user和admin用户登录

  • user用户可以访问 /hello/user
  • admin用户可以访问  /hello/user/hello/admin

3、 整合JWT

目前我们学习的是Spring Security默认的流程认证,会自动完成一系列的认证动作,然后返回前端,所以我们没有办法在认证成功之后做一些扩展,比如想要生成jwt给前端,所以我们需要自己把控框架的认证流程,这块我们就需要拿到框架的一个东西叫做认证管理器AuthenticationManager,有了这个认证管理器,我们可以把控整个认证流程,把控制权牢牢掌握在我们手中,而要想实现这个效果,只需要将loginProcessingUrl方法配置成我们自己的登录接口url,不再使用框架默认提供的/login登录接口,或者不配置loginProcessingUrl方法也可以,然后另外定义一个不需要经过登录校验的接口即可,这就是自定义认证

1. 认证流程

image.png

2. 认证实现

环境准备

 新建一个util包,然后写一个生成jwt的工具类,用于生成和验证JWT令牌

pom文件依赖


<!--JWT-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.8.1</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
<!--工具包-->
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>
3. 定义认证管理器

获取认证管理器AuthenticationManager,而认证管理器AuthenticationManager包装在认证配置类AuthenticationConfiguration中,所以配置如下:


/**
 * 认证管理器
 * @param authenticationConfiguration
 * @return
 * @throws Exception
 */
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
    return authenticationConfiguration.getAuthenticationManager();
}
4. 登录功能实现

修改自定义配置,如果没有配置loginProcessingUrl方法,相当于开启了自定义认证的逻辑,不会走默认直接认证的逻辑,而且由于目前是接口开发,属于前后端分离,无需再使用自定义登录页面,只需要放行登录接口就行


@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    //构建自定义配置
    http.authorizeHttpRequests().antMatchers("/security/login").permitAll();
    http.csrf().disable();
    return http.build();
}

有了认证管理器之后,我们就可以在自己的登录接口中来使用认证管理器完成认证的操作

下面是自定义接口完成认证

package com.itheima.project.web;
import com.itheima.project.entity.LoginDto;
import com.itheima.project.entity.UserAuth;
import com.itheima.project.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
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.HashMap;
import java.util.Map;
/**
 * @author sjqn
 */
@RestController
@RequestMapping("/security")
public class LoginController {
    @Autowired
    private AuthenticationManager authenticationManager;
    @PostMapping("/login")
    public String login(@RequestBody LoginDto loginDto){
        //包装用户的密码
        UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(loginDto.getUsername(),loginDto.getPassword());
        //需要一个认证管理器,来进行调用
        Authentication authentication = authenticationManager.authenticate(upat);
        //判断认证是否成功
        if(authenticate.isAuthenticated()){
            //获取用户
            UserAuth userAuth = (UserAuth) authentication.getPrincipal();
            //认证成功
            //生成jwt,返回
            Map<String,Object> map = new HashMap<>();
            map.put("user",userAuth);
            //过期时间单位为小时
            String token = JwtUtil.createJWT("itcast", 24, map);
            return token;
        }
        return "";
    }
}

LoginDto

package com.itheima.project.entity;
import lombok.Data;
@Data
public class LoginDto {
    private String username;
    private String password;
    
}
测试

使用ApiFox测试

  • 输入正确的用户名和密码,则会返回jwt生成的token,也就说明认证成功了

4、鉴权管理器

当用户登录以后,携带了token访问后端,那么此时Spring Security框架就要对当前请求进行验证,验证包含了两部分,第一验证携带的token是否合法和是否过期,第二验证当前用户是否拥有当前访问资源的权限。

总体思路图

image.png

  1. 自定义授权管理器

我们创建一个类:TokenAuthorizationManager,这个类有两个要求

  • 被spring容器进行管理,所以需要添加@Component注解
  • 实现AuthorizationManager<RequestAuthorizationContext>接口,并重写check方法,在方法中编写校验逻辑
  • 注意方法返回值为new AuthorizationDecision(true)或者null都相当于放行,返回值为new AuthorizationDecision(false)相当于返回403

package com.itheima.project.config;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.itheima.project.entity.UserAuth;
import com.itheima.project.util.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.function.Supplier;
/**
 * @author sjqn
 */
@Component
public class TokenAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext requestAuthorizationContext) {
        //获取request
        HttpServletRequest request = requestAuthorizationContext.getRequest();
        //获取token
        String token = request.getHeader("token");
        if(ObjectUtil.isEmpty(token)){
            return new AuthorizationDecision(false);
        }
        //解析token
        Claims claims = null;
        try{
            claims = JwtUtil.parseJWT("itcast", token);
        }catch(Exception e){
            return new AuthorizationDecision(false);
        }
        if (ObjectUtil.isEmpty(claims)) {
            //token失效
            return new AuthorizationDecision(false);
        }
        //获取userAuth
        UserAuth userAuth = JSONUtil.toBean(JSONUtil.toJsonStr(claims.get("user")), UserAuth.class);
        //存入上下文
        UsernamePasswordAuthenticationToken auth
                =new UsernamePasswordAuthenticationToken(userAuth, userAuth.getPassword(), userAuth.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(auth);
        //获取用户当前的请求地址
        String requestURI = request.getRequestURI();
        //判断地址与对象中的角色是否匹配
        if(userAuth.getRoleList().contains("ADMIN")){
            if("/hello/admin".equals(requestURI)){
                return new AuthorizationDecision(true);
            }
        }
        if(userAuth.getRoleList().contains("USER")){
            if("/hello/user".equals(requestURI)){
                return new AuthorizationDecision(true);
            }
        }
        return new AuthorizationDecision(false);
    }
}
  1. 注册鉴权管理器

鉴权管理器想要生效需要在SecurityConfig中进行注册才能使用


package com.itheima.project.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
/**
 * @author sjqn
 */
@Configuration
public class SecurityConfig {
    @Autowired
    private TokenAuthorizationManager tokenAuthorizationManager;
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests().antMatchers("/security/login").permitAll()
                .anyRequest().access(tokenAuthorizationManager);
        //关闭session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //关闭缓存
        http.headers().cacheControl().disable();
        http.csrf().disable();
        //返回
        return http.build();
    }
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

4. 测试

重启项目之后可以采用apifox进行测试

  • 测试一:
  • 登录账号:user@qq.com     拥有角色:USER
  • 可以访问:/hello/user
  • 其他请求返回403
  • 测试二:
  • 登录账号:admin@qq.com     拥有角色:USER、ADMIN
  • 可以访问:/hello/user、/hello/admin


目录
相关文章
|
2月前
|
资源调度 JavaScript 前端开发
在Vue 3项目中集成Element Plus组件库的步骤
总结起来,在集成过程当中我们关注于库本身提供功能与特性、环境搭建与依赖管理、模块化编程思想以及前端工程化等方面知识点;同时也涵盖前端性能优化(比如上文提及“按需加载”)与定制化开发(例如“自定义主题”)等高级话题.
234 16
|
12月前
|
JavaScript 前端开发
如何在项目中集成 Babel?
如何在项目中集成 Babel?
312 59
|
4月前
|
JSON 分布式计算 大数据
springboot项目集成大数据第三方dolphinscheduler调度器
springboot项目集成大数据第三方dolphinscheduler调度器
247 3
|
4月前
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
383 3
|
4月前
|
Java 关系型数据库 MySQL
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
515 2
|
4月前
|
分布式计算 Java 大数据
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
277 2
|
4月前
|
物联网 Linux 开发者
快速部署自己私有MQTT-Broker-下载安装到运行不到一分钟,快速简单且易于集成到自己项目中
本文给物联网开发的朋友推荐的是GMQT,让物联网开发者快速拥有合适自己的MQTT-Broker,本文从下载程序到安装部署手把手教大家安装用上私有化MQTT服务器。
1255 5
|
分布式计算 大数据 Java
springboot项目集成大数据第三方dolphinscheduler调度器 执行/停止任务
springboot项目集成大数据第三方dolphinscheduler调度器 执行/停止任务
78 0
|
分布式计算 Java 大数据
springboot项目集成dolphinscheduler调度器 项目管理
springboot项目集成dolphinscheduler调度器 项目管理
116 0
|
10月前
|
安全 数据安全/隐私保护
DzzOffice:太完美啦,开源免费Word、Exce、PPT,多人同时协作,最主要还有免费的网盘,将这个项目集成到你的产品里面,项目立刻拥有整套offce解决方案
嗨,大家好,我是小华同学。DzzOffice是一个免费开源的企业协同办公平台,适合中小型企业及团队使用,功能涵盖网盘、文档、表格、演示文稿等,支持企业微信和钉钉移动办公,保障数据私有部署安全。 关注我们,获取更多优质开源项目和高效工作学习方法。
1642 5