<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Security 實現(xiàn)動態(tài)權(quán)限菜單方案(附源碼)

          共 33699字,需瀏覽 68分鐘

           ·

          2022-07-23 16:00

          關(guān)注我們,設(shè)為星標,每天7:40不見不散,架構(gòu)路上與您共享

          回復架構(gòu)師獲取資源


          大家好,我是你們的朋友架構(gòu)君,一個會寫代碼吟詩的架構(gòu)師。

          'javajgs.com';


          系統(tǒng)權(quán)限管理

          1、前言

          在實際開發(fā)中,開發(fā)任何一套系統(tǒng),基本都少不了權(quán)限管理這一塊。這些足以說明權(quán)限管理的重要性。其實SpringSecurity去年就學了,一直沒有時間整理,用了一年多時間了,給我的印象一直都挺好,實用,安全性高(Security可以對密碼進行加密)。而且這一塊在實際開發(fā)中也的確很重要,所以這里整理了一套基于SpringSecurity的權(quán)限管理。

          案例代碼下面有下載鏈接。

          2、案例技術(shù)棧

          如果對于SpringSecurity還不了解的話可以先了解一下SpringSecurity安全控件的學習,頁面采用的是Bootstrap寫的(頁面就簡單的寫了一下,可以根據(jù)自己的需求更改),其實后端理解了,前臺就是顯示作用,大家可以自行更換前臺頁面顯示框架,持久層使用的是Spring-Data-Jpa。

          并且對后端持久層和控制器進行了一下小封裝,Java持久層和控制器的封裝。頁面使用的Thymeleaf模板,SpringBoot整合Thymeleaf模板。

          數(shù)據(jù)庫設(shè)計

          1、表關(guān)系

          • 菜單(TbMenu)=====> 頁面上需要顯示的所有菜單

          • 角色(SysRole)=====> 角色及角色對應(yīng)的菜單

          • 用戶(SysUser)=====> 用戶及用戶對應(yīng)的角色

          • 用戶和角色中間表(sys_user_role)====> 用戶和角色中間表

          2、數(shù)據(jù)庫表結(jié)構(gòu)

          菜單表tb_menu

          角色及菜單權(quán)限表sys_role,其中父節(jié)點parent 為null時為角色,不為null時為對應(yīng)角色的菜單權(quán)限。

          用戶表sys_user

          用戶和角色多對多關(guān)系,用戶和角色中間表sys_user_role(有Spring-Data-Jpa自動生成)。

          新建項目

          1、新建springboot項目

          新建springboot項目,在項目中添加SpringSecurity相關(guān)Maven依賴,pom.map文件

          <?xml version="1.0" encoding="UTF-8"?>
          <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

              <modelVersion>4.0.0</modelVersion>
              <parent>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-parent</artifactId>
                  <version>2.2.2.RELEASE</version>
                  <relativePath/> <!-- lookup parent from repository -->
              </parent>
              <groupId>com.mcy</groupId>
              <artifactId>springboot-security</artifactId>
              <version>0.0.1-SNAPSHOT</version>
              <name>springboot-security</name>
              <description>Demo project for Spring Boot</description>
           
              <properties>
                  <java.version>1.8</java.version>
              </properties>
           
              <dependencies>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-data-jpa</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-security</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-thymeleaf</artifactId>
                  </dependency>
                  <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.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-test</artifactId>
                      <scope>test</scope>
                      <exclusions>
                          <exclusion>
                              <groupId>org.junit.vintage</groupId>
                              <artifactId>junit-vintage-engine</artifactId>
                          </exclusion>
                      </exclusions>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.security</groupId>
                      <artifactId>spring-security-test</artifactId>
                      <scope>test</scope>
                  </dependency>
                  <dependency>
                      <groupId>org.thymeleaf.extras</groupId>
                      <artifactId>thymeleaf-extras-springsecurity5</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-devtools</artifactId>
                      <scope>runtime</scope>
                      <optional>true</optional>
                  </dependency>
                  <dependency>
                      <groupId>org.webjars.bower</groupId>
                      <artifactId>bootstrap-select</artifactId>
                      <version>2.0.0-beta1</version>
                  </dependency>
                  <dependency>
                      <groupId>org.webjars</groupId>
                      <artifactId>bootbox</artifactId>
                      <version>4.4.0</version>
                  </dependency>
              </dependencies>
           
              <build>
                  <plugins>
                      <plugin>
                          <groupId>org.springframework.boot</groupId>
                          <artifactId>spring-boot-maven-plugin</artifactId>
                      </plugin>
                  </plugins>
              </build>
           
          </project>

          2、項目結(jié)構(gòu)

          編寫代碼

          1、編寫實體類

          菜單表實體類TbMenu,Spring-Data-Jpa可以根據(jù)實體類去數(shù)據(jù)庫新建或更新對應(yīng)的表結(jié)構(gòu),詳情可以訪問Spring-Data-Jpa入門:

          https://blog.csdn.net/qq_40205116/article/details/103039936

          import com.fasterxml.jackson.annotation.JsonIgnore;
          import com.mcy.springbootsecurity.custom.BaseEntity;
          import org.springframework.data.annotation.CreatedBy;
           
          import javax.persistence.*;
          import java.util.ArrayList;
          import java.util.List;
           
          /**
           * 菜單表
           * @author
           *
           */

          @Entity
          public class TbMenu extends BaseEntity<Integer{
           private String name;
           private String url;
           private Integer idx;
           @JsonIgnore
           private TbMenu parent;
           @JsonIgnore
           private List<TbMenu> children=new ArrayList<>();
           
           @Column(unique=true)
           public String getName() {
            return name;
           }
           
           public void setName(String name) {
            this.name = name;
           }
           
           public String getUrl() {
            return url;
           }
           
           public void setUrl(String url) {
            this.url = url;
           }
           
           public Integer getIdx() {
            return idx;
           }
           
           public void setIdx(Integer idx) {
            this.idx = idx;
           }
           
           @ManyToOne
           @CreatedBy
           public TbMenu getParent() {
            return parent;
           }
           
           public void setParent(TbMenu parent) {
            this.parent = parent;
           }
           
           @OneToMany(cascade=CascadeType.ALL,mappedBy="parent")
           @OrderBy(value="idx")
           public List<TbMenu> getChildren() {
            return children;
           }
           
           public void setChildren(List<TbMenu> children) {
            this.children = children;
           }
           
           public TbMenu(Integer id) {
            super(id);
           }
           
           public TbMenu(){
            super();
           }
           
           public TbMenu(String name, String url, Integer idx, TbMenu parent, List<TbMenu> children) {
            this.name = name;
            this.url = url;
            this.idx = idx;
            this.parent = parent;
            this.children = children;
           }
           
           public TbMenu(Integer integer, String name, String url, Integer idx, TbMenu parent, List<TbMenu> children) {
            super(integer);
            this.name = name;
            this.url = url;
            this.idx = idx;
            this.parent = parent;
            this.children = children;
           }
           
           @Transient
           public Integer getParentId() {
            return parent==null?null:parent.getId();
           }
          }

          表新建好了,下面就是實現(xiàn)增刪改查就可以了,實現(xiàn)效果如下。

          新增和修改菜單。

          對于Bootstrap的樹形表格,可以移步到:BootStrap-bable-treegrid樹形表格的使用。

          https://blog.csdn.net/qq_40205116/article/details/103740104

          菜單管理實現(xiàn)了,下一步就是實現(xiàn)角色及角色對應(yīng)的權(quán)限管理了。

          角色及權(quán)限表SysRole,parent 為null時為角色,不為null時為權(quán)限。

          package com.mcy.springbootsecurity.entity;
           
          import com.fasterxml.jackson.annotation.JsonIgnore;
          import com.mcy.springbootsecurity.custom.BaseEntity;
          import org.springframework.data.annotation.CreatedBy;
          import javax.persistence.*;
          import java.util.ArrayList;
          import java.util.List;
           
          @Entity
          /***
           * 角色及角色對應(yīng)的菜單權(quán)限
           * @author
           *parent 為null時為角色,不為null時為權(quán)限
           */

          public class SysRole extends BaseEntity<Integer{
           private String name; //名稱
           private String code; //代碼
           @JsonIgnore
           private SysRole parent;
           private Integer idx; //排序
           @JsonIgnore
           private List<SysRole> children = new ArrayList<>();
           
           @Column(length=20)
           public String getName() {
            return name;
           }
           
           public void setName(String name) {
            this.name = name;
           }
           
           public String getCode() {
            return code;
           }
           
           public void setCode(String code) {
            this.code = code;
           }
           
           @ManyToOne
           @CreatedBy
           public SysRole getParent() {
            return parent;
           }
           
           public void setParent(SysRole parent) {
            this.parent = parent;
           }
           
           @OneToMany(cascade=CascadeType.ALL,mappedBy="parent")
           public List<SysRole> getChildren() {
            return children;
           }
           
           public void setChildren(List<SysRole> children) {
            this.children = children;
           }
           
           //獲取父節(jié)點id
           @Transient
           public Integer getParentId() {
            return parent==null?null:parent.getId();
           }
           
           public Integer getIdx() {
            return idx;
           }
           
           public void setIdx(Integer idx) {
            this.idx = idx;
           }
           
           public SysRole(String name, String code, SysRole parent, Integer idx, List<SysRole> children) {
            this.name = name;
            this.code = code;
            this.parent = parent;
            this.idx = idx;
            this.children = children;
           }
           
           public SysRole(Integer id, String name, String code, SysRole parent, Integer idx, List<SysRole> children) {
            super(id);
            this.name = name;
            this.code = code;
            this.parent = parent;
            this.idx = idx;
            this.children = children;
           }
           
           public SysRole(Integer id) {
            super(id);
           }
           
           public SysRole(){}
          }

          首先需要實現(xiàn)角色管理,之后在角色中添加對應(yīng)的菜單權(quán)限。

          實現(xiàn)效果(也可以和菜單管理一樣,用樹形表格展示,根據(jù)個人需求。這里用的是樹形菜單展示的)。

          給角色分配權(quán)限。

          最后實現(xiàn)的就是用戶管理了,只需要對添加的用戶分配對應(yīng)的角色就可以了,用戶登錄時,顯示角色對應(yīng)的權(quán)限。

          用戶表SysUser,繼承的BaseEntity類中就一個ID字段。

          import com.fasterxml.jackson.annotation.JsonIgnore;
          import com.mcy.springbootsecurity.custom.BaseEntity;
           
          import javax.persistence.*;
          import java.util.ArrayList;
          import java.util.List;
           
          /**
           * 用戶表
           */

          @Entity
          public class SysUser extends BaseEntity<Integer{
           private String username; //賬號
           private String password; //密碼
           private String name;  //姓名
           private String address;  //地址
           
           @JsonIgnore
           private List<SysRole> roles=new ArrayList<>(); //角色
           
           @Column(length=20,unique=true)
           public String getUsername() {
            return username;
           }
           public void setUsername(String username) {
            this.username = username;
           }
           
           @Column(length=100)
           public String getPassword() {
            return password;
           }
           public void setPassword(String password) {
            this.password = password;
           }
           
           @Column(length=20)
           public String getName() {
            return name;
           }
           public void setName(String name) {
            this.name = name;
           }
           
           @ManyToMany(cascade=CascadeType.REFRESH,fetch=FetchType.EAGER)
           @JoinTable(name="sys_user_role",joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="role_id"))
           public List<SysRole> getRoles() {
            return roles;
           }
           public void setRoles(List<SysRole> roles) {
            this.roles = roles;
           }
           
           public String getAddress() {
            return address;
           }
           
           public void setAddress(String address) {
            this.address = address;
           }
           
           //角色名稱
           @Transient
           public String getRoleNames() {
            String str="";
            for (SysRole role : getRoles()) {
             str+=role.getName()+",";
            }
            if(str.length()>0) {
             str=str.substring(0, str.length()-1);
            }
            return str;
           }
           
           //角色代碼
           @Transient
           public String getRoleCodes() {
            String str="";
            for (SysRole role : getRoles()) {
             str+=role.getCode()+",";
            }
            if(str.indexOf(",")>0) {
             str=str.substring(0,str.length()-1);
            }
            return str;
           }
           
          }

          用戶管理就基本的數(shù)據(jù)表格,效果如圖。

          2、Security配置文件

          Security相關(guān)配置文件,下面兩個文件如果看不懂,可以訪問SpringSecurity安全控件的學習中有詳細講解。

          https://blog.csdn.net/qq_40205116/article/details/103439326

          package com.mcy.springbootsecurity.security;
           
          import com.mcy.springbootsecurity.service.SysUserService;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
          import org.springframework.security.config.annotation.web.builders.HttpSecurity;
          import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
          import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
           
          @Configuration
          public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
           
              @Autowired
              private SysUserService userService;
           
              /**
               * 用戶認證操作
               * @param auth
               * @throws Exception
               */

              @Override
              protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                  //添加用戶,并給予權(quán)限
                  auth.inMemoryAuthentication().withUser("aaa").password("{noop}1234").roles("DIY");
                  //設(shè)置認證方式
                  auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
              }
           
              /**
               * 用戶授權(quán)操作
               * @param http
               * @throws Exception
               */

              @Override
              protected void configure(HttpSecurity http) throws Exception {
                  http.csrf().disable();    //安全器令牌
                  http.formLogin()
                          //登錄請求被攔截
                          .loginPage("/login").permitAll()
                          //設(shè)置默認登錄成功跳轉(zhuǎn)頁面
                          .successForwardUrl("/main")
                          .failureUrl("/login?error");   //登錄失敗的頁面
                  http.authorizeRequests().antMatchers("/static/**""/assets/**").permitAll();    //文件下的所有都能訪問
                  http.authorizeRequests().antMatchers("/webjars/**").permitAll();
                  http.logout().logoutUrl("/logout").permitAll();     //退出
                  http.authorizeRequests().anyRequest().authenticated();    //除此之外的都必須通過請求驗證才能訪問
              }
          }

          獲取登錄者相關(guān)信息,工具類。

          import com.mcy.springbootsecurity.entity.SysUser;
          import com.mcy.springbootsecurity.service.SysUserService;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.security.core.GrantedAuthority;
          import org.springframework.security.core.context.SecurityContextHolder;
          import org.springframework.security.core.userdetails.UserDetails;
          import org.springframework.stereotype.Component;
           
          import java.util.ArrayList;
          import java.util.List;
           
          //創(chuàng)建會話,獲取當前登錄對象
          @Component
          public class UserUtils {
           @Autowired
           private SysUserService userService;
           
           /**
            * 獲取當前登錄者的信息
            * @return 當前者信息
            */

           public SysUser getUser() {
            //獲取當前用戶的用戶名
            String username = SecurityContextHolder.getContext().getAuthentication().getName();
            SysUser user = userService.findByUsername(username);
            return user;
           }
           
           /**
            * 判斷此用戶中是否包含roleName菜單權(quán)限
            * @param roleName
            * @return
            */

           public Boolean hasRole(String roleName) {
            //獲取UserDetails類,
            UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            List<String> roleCodes=new ArrayList<>();
            for (GrantedAuthority authority : userDetails.getAuthorities()) {
             //getAuthority()返回用戶對應(yīng)的菜單權(quán)限
             roleCodes.add(authority.getAuthority());
            }
            return roleCodes.contains(roleName);
           }
          }

          3、動態(tài)權(quán)限菜單加載相關(guān)方法

          用戶表的SysUserService需要實現(xiàn)UserDetailsService接口,因為在SpringSecurity中配置的相關(guān)參數(shù)需要是UserDetailsService類的數(shù)據(jù)。

          重寫UserDetailsService接口中的loadUserByUsername方法,通過該方法查詢對應(yīng)的用戶,返回對象UserDetails是SpringSecurity的一個核心接口。其中定義了一些可以獲取用戶名,密碼,權(quán)限等與認證相關(guān)信息的方法。

          重寫的loadUserByUsername方法。

          @Override
          public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
              //調(diào)用持久層接口findByUsername方法查詢用戶。
              SysUser user = userRepository.findByUsername(username);
              if(user == null){
                  throw new UsernameNotFoundException("用戶名不存在");
              }
              //創(chuàng)建List集合,用來保存用戶菜單權(quán)限,GrantedAuthority對象代表賦予當前用戶的權(quán)限
              List<GrantedAuthority> authorities = new ArrayList<>();
              //獲得當前用戶角色集合
              List<SysRole> roles = user.getRoles();
              List<SysRole> haveRoles=new ArrayList<>();
              for (SysRole role : roles) {
                  haveRoles.add(role);
                  List<SysRole> children = roleService.findByParent(role);
                  children.removeAll(haveRoles);
                  haveRoles.addAll(children);
              }
              for(SysRole role: haveRoles){
                  //將關(guān)聯(lián)對象role的name屬性保存為用戶的認證權(quán)限
                  authorities.add(new SimpleGrantedAuthority(role.getName()));
              }
              //此處返回的是org.springframework.security.core.userdetails.User類,該類是SpringSecurity內(nèi)部的實現(xiàn)
              //org.springframework.security.core.userdetails.User類實現(xiàn)了UserDetails接口
              return new User(user.getUsername(), user.getPassword(), authorities);
          }

          所有功能實現(xiàn)了,最后就是根據(jù)角色去顯示對應(yīng)的菜單了。

          TbMenuService類中的findAuditMenu方法,查詢當前用戶所擁有的權(quán)限菜單。

          /**
           * 獲取用戶所擁有的權(quán)限對應(yīng)的菜單項
           * @return
           */

          public List<TbMenu> findAuditMenu() {
              List<TbMenu> menus;
              //判斷是否是后門用戶
              if(userUtils.hasRole("ROLE_DIY")){
                  //查詢所有菜單,子菜單可以通過父級菜單的映射得到
                  menus = menuRepository.findByParentIsNullOrderByIdx();
              }else{
                  //獲取此用戶對應(yīng)的菜單權(quán)限
                  menus = auditMenu(menuRepository.findByParentIsNullOrderByIdx());
              }
              return menus;
          }
           
          //根據(jù)用戶的菜單權(quán)限對菜單進行過濾
          private List<TbMenu> auditMenu(List<TbMenu> menus) {
              List<TbMenu> list = new ArrayList<>();
              for(TbMenu menu: menus){
                  String name = menu.getName();
                  //判斷此用戶是否有此菜單權(quán)限
                  if(userUtils.hasRole(name)){
                      list.add(menu);
                      //遞歸判斷子菜單
                      if(menu.getChildren() != null && !menu.getChildren().isEmpty()) {
                          menu.setChildren(auditMenu(menu.getChildren()));
                      }
                  }
              }
              return list;
          }

          在UserUtils工具類中的hasRole方法,判斷此用戶中是否包含roleName菜單權(quán)限。

          public Boolean hasRole(String roleName) {
           //獲取UserDetails類,
           UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
           List<String> roleCodes=new ArrayList<>();
           for (GrantedAuthority authority : userDetails.getAuthorities()) {
            //getAuthority()返回用戶對應(yīng)的菜單權(quán)限
            roleCodes.add(authority.getAuthority());
           }
           return roleCodes.contains(roleName);
          }

          之后在控制器中返回用戶對應(yīng)的菜單權(quán)限,之后在前臺頁面遍歷就可以了。

          @RequestMapping(value = "/main")
          public String main(ModelMap map){
              //加載菜單
              List<TbMenu> menus = menuService.findAuditMenu();
              map.put("menus", menus);
              if (menus.isEmpty()) {
                  return "main/main";
              }
              return "main/main1";
          }

          4、首頁菜單遍歷

          首頁菜單遍歷,這里使用的是LayUI菜單,如果其他框架可以自行根據(jù)頁面標簽規(guī)律遍歷,因為頁面使用的是Thymeleaf模板,不是JSP,使用遍歷菜單時不是采用的EL表達式,而是使用的Thymeleaf自帶的標簽表達式。

          <div id="main">
              <div id="main_nav">
                  <div class="panel-group" id="accordion" style="margin-bottom: 0;">
                      <div th:each="menu, menuStat: ${menus}" th:if="${menu.children.size() != 0 && menu.children != null}" class="panel panel-default">
                          <div class="panel-heading">
                              <h4 class="panel-title">
                                  <p data-toggle="collapse" data-parent="#accordion" th:href="|#collapseOne${menuStat.index}|">
                                      <span th:text="${menu.name}">系統(tǒng)設(shè)置</span><span class="caret"></span>
                                  </p>
                              </h4>
                          </div>
                          <div th:if="${menuStat.first}" th:id="|collapseOne${menuStat.index}|" class="panel-collapse collapse collapse in">
                              <div class="panel-body">
                                  <p th:each="subMenu:${menu.children}" th:src="${subMenu.url}" th:text="${subMenu.name}">菜單管理</p>
                              </div>
                          </div>
                          <div th:if="${!menuStat.first}" th:id="|collapseOne${menuStat.index}|" class="panel-collapse collapse collapse">
                              <div class="panel-body">
                                  <p th:each="subMenu:${menu.children}" th:src="${subMenu.url}" th:text="${subMenu.name}">菜單管理</p>
                              </div>
                          </div>
                      </div>
                  </div>
                  <div id="nav_p">
                      <p th:each="menu:${menus}" th:if="${menu.children.size() == 0}" th:src="${menu.url}" th:text="${menu.name}">成績管理</p>
                  </div>
              </div>
              <div id="main_home">
                  首頁內(nèi)容
              </div>
          </div>

          測試應(yīng)用

          1、對應(yīng)效果展示

          用戶數(shù)據(jù)及對應(yīng)的角色

          管理員對應(yīng)的菜單權(quán)限。

          用戶角色對應(yīng)的菜單權(quán)限。

          測試用戶角色對應(yīng)的菜單權(quán)限。

          2、測試應(yīng)用

          用戶名為admin1有管理員角色的用戶登錄,菜單顯示。

          用戶名為admin2有用戶角色的用戶登錄,菜單顯示。

          用戶名為admin3有測試用戶角色的用戶登錄,菜單顯示。

          3、案例代碼下載

          下載地址:https://github.com/machaoyin/SpringBoot-Security

          來源:blog.csdn.net/qq_40205116/article/details/103739978


          到此文章就結(jié)束了。Java架構(gòu)師必看一個集公眾號、小程序、網(wǎng)站(3合1的文章平臺,給您架構(gòu)路上一臂之力,javajgs.com)。如果今天的文章對你在進階架構(gòu)師的路上有新的啟發(fā)和進步,歡迎轉(zhuǎn)發(fā)給更多人。歡迎加入架構(gòu)師社區(qū)技術(shù)交流群,眾多大咖帶你進階架構(gòu)師,在后臺回復“加群”即可入群。



          這些年小編給你分享過的干貨


          1.idea永久激活碼(親測可用)

          2.優(yōu)質(zhì)ERP系統(tǒng)帶進銷存財務(wù)生產(chǎn)功能(附源碼)

          3.優(yōu)質(zhì)SpringBoot帶工作流管理項目(附源碼)

          4.最好用的OA系統(tǒng),拿來即用(附源碼)

          5.SBoot+Vue外賣系統(tǒng)前后端都有(附源碼

          6.SBoot+Vue可視化大屏拖拽項目(附源碼)


          轉(zhuǎn)發(fā)在看就是最大的支持??

          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久伊人网老师机激情 | 色天堂在线播放 | 日韩三级中文字幕电影在线 | 欧美精品久久久久久久久久 | 视频一区在线看 |