欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Spring Boot Security工作流程

 更新時(shí)間:2022年05月06日 09:53:35   投稿:mrr  
Spring Security,這是一種基于 Spring AOP 和 Servlet 。這篇文章主要介紹了Spring Boot Security的相關(guān)知識(shí),需要的朋友可以參考下

簡(jiǎn)介

Spring Security,這是一種基于 Spring AOP 和 Servlet 過(guò)濾器的安全框架。它提供全面的安全性解決方案,同時(shí)在 Web 請(qǐng)求級(jí)和方法調(diào)用級(jí)處理身份確認(rèn)和授權(quán)。

工作流程

從網(wǎng)上找了一張Spring Security 的工作流程圖,如下。

圖中標(biāo)記的MyXXX,就是我們項(xiàng)目中需要配置的。

快速上手

建表

表結(jié)構(gòu)

建表語(yǔ)句

DROP TABLE IF EXISTS `user`;
DROP TABLE IF EXISTS `role`;
DROP TABLE IF EXISTS `user_role`;
DROP TABLE IF EXISTS `role_permission`;
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
PRIMARY KEY (`id`) 
);
CREATE TABLE `role` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`) 
);
CREATE TABLE `user_role` (
`user_id` bigint(11) NOT NULL,
`role_id` bigint(11) NOT NULL
);
CREATE TABLE `role_permission` (
`role_id` bigint(11) NOT NULL,
`permission_id` bigint(11) NOT NULL
);
CREATE TABLE `permission` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`url` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`description` varchar(255) NULL,
`pid` bigint(11) NOT NULL,
PRIMARY KEY (`id`) 
);
INSERT INTO user (id, username, password) VALUES (1,'user','e10adc3949ba59abbe56e057f20f883e'); 
INSERT INTO user (id, username , password) VALUES (2,'admin','e10adc3949ba59abbe56e057f20f883e'); 
INSERT INTO role (id, name) VALUES (1,'USER');
INSERT INTO role (id, name) VALUES (2,'ADMIN');
INSERT INTO permission (id, url, name, pid) VALUES (1,'/user/common','common',0);
INSERT INTO permission (id, url, name, pid) VALUES (2,'/user/admin','admin',0);
INSERT INTO user_role (user_id, role_id) VALUES (1, 1);
INSERT INTO user_role (user_id, role_id) VALUES (2, 1);
INSERT INTO user_role (user_id, role_id) VALUES (2, 2);
INSERT INTO role_permission (role_id, permission_id) VALUES (1, 1);
INSERT INTO role_permission (role_id, permission_id) VALUES (2, 1);
INSERT INTO role_permission (role_id, permission_id) VALUES (2, 2);

pom.xml

<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>org.thymeleaf.extras</groupId>
 <artifactId>thymeleaf-extras-security4</artifactId>
</dependency>

User

public class User implements UserDetails , Serializable {
 private Long id;
 private String username;
 private String password;
 private List<Role> authorities;
 public Long getId() {
 return id;
 }
 public void setId(Long id) {
 this.id = id;
 }
 @Override
 public String getUsername() {
 return username;
 }
 public void setUsername(String username) {
 this.username = username;
 }
 @Override
 public String getPassword() {
 return password;
 }
 public void setPassword(String password) {
 this.password = password;
 }
 @Override
 public List<Role> getAuthorities() {
 return authorities;
 }
 public void setAuthorities(List<Role> authorities) {
 this.authorities = authorities;
 }
 /**
 * 用戶賬號(hào)是否過(guò)期
 */
 @Override
 public boolean isAccountNonExpired() {
 return true;
 }
 /**
 * 用戶賬號(hào)是否被鎖定
 */
 @Override
 public boolean isAccountNonLocked() {
 return true;
 }
 /**
 * 用戶密碼是否過(guò)期
 */
 @Override
 public boolean isCredentialsNonExpired() {
 return true;
 }
 /**
 * 用戶是否可用
 */
 @Override
 public boolean isEnabled() {
 return true;
 }
}

上面的 User 類實(shí)現(xiàn)了 UserDetails 接口,該接口是實(shí)現(xiàn)Spring Security 認(rèn)證信息的核心接口。其中 getUsername 方法為 UserDetails 接口 的方法,這個(gè)方法返回 username,也可以是其他的用戶信息,例如手機(jī)號(hào)、郵箱等。getAuthorities() 方法返回的是該用戶設(shè)置的權(quán)限信息,在本實(shí)例中,從數(shù)據(jù)庫(kù)取出用戶的所有角色信息,權(quán)限信息也可以是用戶的其他信息,不一定是角色信息。另外需要讀取密碼,最后幾個(gè)方法一般情況下都返回 true,也可以根據(jù)自己的需求進(jìn)行業(yè)務(wù)判斷。

Role

public class Role implements GrantedAuthority {
 private Long id;
 private String name;
 public Long getId() {
 return id;
 }
 public void setId(Long id) {
 this.id = id;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 @Override
 public String getAuthority() {
 return name;
 }
}

Role 類實(shí)現(xiàn)了 GrantedAuthority 接口,并重寫 getAuthority() 方法。權(quán)限點(diǎn)可以為任何字符串,不一定是非要用角色名。

所有的Authentication實(shí)現(xiàn)類都保存了一個(gè)GrantedAuthority列表,其表示用戶所具有的權(quán)限。GrantedAuthority是通過(guò)AuthenticationManager設(shè)置到Authentication對(duì)象中的,然后AccessDecisionManager將從Authentication中獲取用戶所具有的GrantedAuthority來(lái)鑒定用戶是否具有訪問(wèn)對(duì)應(yīng)資源的權(quán)限。

MyUserDetailsService

@Service
public class MyUserDetailsService implements UserDetailsService {
 @Autowired
 private UserMapper userMapper;
 @Autowired
 private RoleMapper roleMapper;
 @Override
 public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
 //查數(shù)據(jù)庫(kù)
 User user = userMapper.loadUserByUsername( userName );
 if (null != user) {
  List<Role> roles = roleMapper.getRolesByUserId( user.getId() );
  user.setAuthorities( roles );
 }
 return user;
 }
}

Service 層需要實(shí)現(xiàn) UserDetailsService 接口,該接口是根據(jù)用戶名獲取該用戶的所有信息, 包括用戶信息和權(quán)限點(diǎn)。

MyInvocationSecurityMetadataSourceService

@Component
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {

 @Autowired
 private PermissionMapper permissionMapper;

 /**
 * 每一個(gè)資源所需要的角色 Collection<ConfigAttribute>決策器會(huì)用到
 */
 private static HashMap<String, Collection<ConfigAttribute>> map =null;


 /**
 * 返回請(qǐng)求的資源需要的角色
 */
 @Override
 public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
 if (null == map) {
  loadResourceDefine();
 }
 //object 中包含用戶請(qǐng)求的request 信息
 HttpServletRequest request = ((FilterInvocation) o).getHttpRequest();
 for (Iterator<String> it = map.keySet().iterator() ; it.hasNext();) {
  String url = it.next();
  if (new AntPathRequestMatcher( url ).matches( request )) {
  return map.get( url );
  }
 }

 return null;
 }

 @Override
 public Collection<ConfigAttribute> getAllConfigAttributes() {
 return null;
 }

 @Override
 public boolean supports(Class<?> aClass) {
 return true;
 }

 /**
 * 初始化 所有資源 對(duì)應(yīng)的角色
 */
 public void loadResourceDefine() {
 map = new HashMap<>(16);
 //權(quán)限資源 和 角色對(duì)應(yīng)的表 也就是 角色權(quán)限 中間表
 List<RolePermisson> rolePermissons = permissionMapper.getRolePermissions();

 //某個(gè)資源 可以被哪些角色訪問(wèn)
 for (RolePermisson rolePermisson : rolePermissons) {

  String url = rolePermisson.getUrl();
  String roleName = rolePermisson.getRoleName();
  ConfigAttribute role = new SecurityConfig(roleName);

  if(map.containsKey(url)){
  map.get(url).add(role);
  }else{
  List<ConfigAttribute> list = new ArrayList<>();
  list.add( role );
  map.put( url , list );
  }
 }
 }
}

MyInvocationSecurityMetadataSourceService 類實(shí)現(xiàn)了 FilterInvocationSecurityMetadataSource,F(xiàn)ilterInvocationSecurityMetadataSource 的作用是用來(lái)儲(chǔ)存請(qǐng)求與權(quán)限的對(duì)應(yīng)關(guān)系。

FilterInvocationSecurityMetadataSource接口有3個(gè)方法:

boolean supports(Class<?> clazz):指示該類是否能夠?yàn)橹付ǖ姆椒ㄕ{(diào)用或Web請(qǐng)求提供ConfigAttributes。

Collection

 getAllConfigAttributes():Spring容器啟動(dòng)時(shí)自動(dòng)調(diào)用, 一般把所有請(qǐng)求與權(quán)限的對(duì)應(yīng)關(guān)系也要在這個(gè)方法里初始化, 保存在一個(gè)屬性變量里。

Collection

 getAttributes(Object object):當(dāng)接收到一個(gè)http請(qǐng)求時(shí), filterSecurityInterceptor會(huì)調(diào)用的方法. 參數(shù)object是一個(gè)包含url信息的HttpServletRequest實(shí)例. 這個(gè)方法要返回請(qǐng)求該url所需要的所有權(quán)限集合。

MyAccessDecisionManager

/**
 * 決策器
 */
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {

 private final static Logger logger = LoggerFactory.getLogger(MyAccessDecisionManager.class);

 /**
 * 通過(guò)傳遞的參數(shù)來(lái)決定用戶是否有訪問(wèn)對(duì)應(yīng)受保護(hù)對(duì)象的權(quán)限
 *
 * @param authentication 包含了當(dāng)前的用戶信息,包括擁有的權(quán)限。這里的權(quán)限來(lái)源就是前面登錄時(shí)UserDetailsService中設(shè)置的authorities。
 * @param object 就是FilterInvocation對(duì)象,可以得到request等web資源
 * @param configAttributes configAttributes是本次訪問(wèn)需要的權(quán)限
 */
 @Override
 public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
 if (null == configAttributes || 0 >= configAttributes.size()) {
  return;
 } else {
  String needRole;
  for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
  needRole = iter.next().getAttribute();

  for(GrantedAuthority ga : authentication.getAuthorities()) {
   if(needRole.trim().equals(ga.getAuthority().trim())) {
   return;
   }
  }
  }
  throw new AccessDeniedException("當(dāng)前訪問(wèn)沒(méi)有權(quán)限");
 }

 }

 /**
 * 表示此AccessDecisionManager是否能夠處理傳遞的ConfigAttribute呈現(xiàn)的授權(quán)請(qǐng)求
 */
 @Override
 public boolean supports(ConfigAttribute configAttribute) {
 return true;
 }

 /**
 * 表示當(dāng)前AccessDecisionManager實(shí)現(xiàn)是否能夠?yàn)橹付ǖ陌踩珜?duì)象(方法調(diào)用或Web請(qǐng)求)提供訪問(wèn)控制決策
 */
 @Override
 public boolean supports(Class<?> aClass) {
 return true;
 }}

MyAccessDecisionManager 類實(shí)現(xiàn)了AccessDecisionManager接口,AccessDecisionManager是由AbstractSecurityInterceptor調(diào)用的,它負(fù)責(zé)鑒定用戶是否有訪問(wèn)對(duì)應(yīng)資源(方法或URL)的權(quán)限。

MyFilterSecurityInterceptor

@Component
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
 @Autowired
 private FilterInvocationSecurityMetadataSource securityMetadataSource;
 @Autowired
 public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
 super.setAccessDecisionManager(myAccessDecisionManager);
 }
 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
 FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
 invoke(fi);
 }
 public void invoke(FilterInvocation fi) throws IOException, ServletException {
 InterceptorStatusToken token = super.beforeInvocation(fi);
 try {
  //執(zhí)行下一個(gè)攔截器
  fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
 } finally {
  super.afterInvocation(token, null);
 }
 }
 @Override
 public Class<?> getSecureObjectClass() {
 return FilterInvocation.class;
 }
 @Override
 public SecurityMetadataSource obtainSecurityMetadataSource() {
 return this.securityMetadataSource;
 }
}

每種受支持的安全對(duì)象類型(方法調(diào)用或Web請(qǐng)求)都有自己的攔截器類,它是AbstractSecurityInterceptor的子類,AbstractSecurityInterceptor 是一個(gè)實(shí)現(xiàn)了對(duì)受保護(hù)對(duì)象的訪問(wèn)進(jìn)行攔截的抽象類。

AbstractSecurityInterceptor的機(jī)制可以分為幾個(gè)步驟:

1. 查找與當(dāng)前請(qǐng)求關(guān)聯(lián)的“配置屬性(簡(jiǎn)單的理解就是權(quán)限)”
2. 將 安全對(duì)象(方法調(diào)用或Web請(qǐng)求)、當(dāng)前身份驗(yàn)證、配置屬性 提交給決策器(AccessDecisionManager)
3. (可選)更改調(diào)用所根據(jù)的身份驗(yàn)證
4. 允許繼續(xù)進(jìn)行安全對(duì)象調(diào)用(假設(shè)授予了訪問(wèn)權(quán))
5. 在調(diào)用返回之后,如果配置了AfterInvocationManager。如果調(diào)用引發(fā)異常,則不會(huì)調(diào)用AfterInvocationManager。

AbstractSecurityInterceptor中的方法說(shuō)明:

beforeInvocation()方法實(shí)現(xiàn)了對(duì)訪問(wèn)受保護(hù)對(duì)象的權(quán)限校驗(yàn),內(nèi)部用到了AccessDecisionManager和AuthenticationManager;

finallyInvocation()方法用于實(shí)現(xiàn)受保護(hù)對(duì)象請(qǐng)求完畢后的一些清理工作,主要是如果在beforeInvocation()中改變了SecurityContext,則在finallyInvocation()中需要將其恢復(fù)為原來(lái)的SecurityContext,該方法的調(diào)用應(yīng)當(dāng)包含在子類請(qǐng)求受保護(hù)資源時(shí)的finally語(yǔ)句塊中。

afterInvocation()方法實(shí)現(xiàn)了對(duì)返回結(jié)果的處理,在注入了AfterInvocationManager的情況下默認(rèn)會(huì)調(diào)用其decide()方法。
了解了AbstractSecurityInterceptor,就應(yīng)該明白了,我們自定義MyFilterSecurityInterceptor就是想使用我們之前自定義的 AccessDecisionManager 和 securityMetadataSource。

SecurityConfig

@EnableWebSecurity注解以及WebSecurityConfigurerAdapter一起配合提供基于web的security。自定義類 繼承了WebSecurityConfigurerAdapter來(lái)重寫了一些方法來(lái)指定一些特定的Web安全設(shè)置。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 @Autowired
 private MyUserDetailsService userService;
 @Autowired
 public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
  //校驗(yàn)用戶
  auth.userDetailsService( userService ).passwordEncoder( new PasswordEncoder() {
   //對(duì)密碼進(jìn)行加密
   @Override
   public String encode(CharSequence charSequence) {
    System.out.println(charSequence.toString());
    return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
   }
   //對(duì)密碼進(jìn)行判斷匹配
   @Override
   public boolean matches(CharSequence charSequence, String s) {
    String encode = DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
    boolean res = s.equals( encode );
    return res;
   }
  } );
 }
 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http.authorizeRequests()
    .antMatchers("/","index","/login","/login-error","/401","/css/**","/js/**").permitAll()
    .anyRequest().authenticated()
    .and()
    .formLogin().loginPage( "/login" ).failureUrl( "/login-error" )
    .and()
    .exceptionHandling().accessDeniedPage( "/401" );
  http.logout().logoutSuccessUrl( "/" );
 }
}

MainController

@Controller
public class MainController {
 @RequestMapping("/")
 public String root() {
  return "redirect:/index";
 }
 @RequestMapping("/index")
 public String index() {
  return "index";
 }
 @RequestMapping("/login")
 public String login() {
  return "login";
 }
 @RequestMapping("/login-error")
 public String loginError(Model model) {
  model.addAttribute( "loginError" , true);
  return "login";
 }
 @GetMapping("/401")
 public String accessDenied() {
  return "401";
 }
 @GetMapping("/user/common")
 public String common() {
  return "user/common";
 }
 @GetMapping("/user/admin")
 public String admin() {
  return "user/admin";
 }
}

頁(yè)面

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<head>
 <meta charset="UTF-8">
 <title>首頁(yè)</title>
</head>
<body>
 <h2>page list</h2>
 <a href="/user/common" rel="external nofollow" rel="external nofollow" >common page</a>
 

 <a href="/user/admin" rel="external nofollow" rel="external nofollow" >admin page</a>
 

 <form th:action="@{/logout}" method="post">
  <input type="submit" class="btn btn-primary" value="注銷"/>
 </form>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<head>
 <meta charset="UTF-8">
 <title>首頁(yè)</title>
</head>
<body>
 <h2>page list</h2>
 <a href="/user/common" rel="external nofollow" rel="external nofollow" >common page</a>

 <a href="/user/admin" rel="external nofollow" rel="external nofollow" >admin page</a>

 <form th:action="@{/logout}" method="post">
  <input type="submit" class="btn btn-primary" value="注銷"/>
 </form>
</body>
</html>

admin.html

<!DOCTYPE html>
<head>
 <meta charset="UTF-8">
 <title>admin page</title>
</head>
<body>
 success admin page?。?!
</body>
</html>

common.html

<!DOCTYPE html>
<head>
 <meta charset="UTF-8">
 <title>common page</title>
</head>
<body>
 success common page!?。?
</body>
</html>

401.html

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>401 page</title>
</head>
<body>
 <div>
  <div>
   <h2>權(quán)限不夠</h2>
   <p>拒絕訪問(wèn)!</p>
  </div>
 </div>
</body>
</html>

最后運(yùn)行項(xiàng)目,可以分別用 user、admin 賬號(hào) 去測(cè)試認(rèn)證和授權(quán)是否正確。

總結(jié)

以上所述是小編給大家介紹的Spring Boot Security,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Java 如何從list中刪除符合條件的數(shù)據(jù)

    Java 如何從list中刪除符合條件的數(shù)據(jù)

    這篇文章主要介紹了Java 如何從list中刪除符合條件的數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Spring中的注解@Autowired實(shí)現(xiàn)過(guò)程全解(@Autowired 背后的故事)

    Spring中的注解@Autowired實(shí)現(xiàn)過(guò)程全解(@Autowired 背后的故事)

    這篇文章主要介紹了Spring中的注解@Autowired實(shí)現(xiàn)過(guò)程全解,給大家聊聊@Autowired 背后的故事及實(shí)現(xiàn)原理,需要的朋友可以參考下
    2021-07-07
  • JavaWeb入門教程之分頁(yè)查詢功能的簡(jiǎn)單實(shí)現(xiàn)

    JavaWeb入門教程之分頁(yè)查詢功能的簡(jiǎn)單實(shí)現(xiàn)

    這篇文章主要介紹了JavaWeb入門教程之分頁(yè)查詢功能的簡(jiǎn)單實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Java多線程中的ReentrantLock可中斷鎖詳細(xì)解讀

    Java多線程中的ReentrantLock可中斷鎖詳細(xì)解讀

    這篇文章主要介紹了Java多線程中的ReentrantLock可中斷鎖詳細(xì)解讀,ReentrantLock中的lockInterruptibly()方法使得線程可以在被阻塞時(shí)響應(yīng)中斷,比如一個(gè)線程t1通過(guò)lockInterruptibly()方法獲取到一個(gè)可重入鎖,并執(zhí)行一個(gè)長(zhǎng)時(shí)間的任務(wù),需要的朋友可以參考下
    2023-12-12
  • IDEA熱部署配置詳細(xì)教程

    IDEA熱部署配置詳細(xì)教程

    這篇文章主要介紹了IDEA熱部署配置詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • springboot2.5.0和redis整合配置詳解

    springboot2.5.0和redis整合配置詳解

    本篇文章向大家介紹springboot2.5.0 整合 redis 配置方法,教大家在pom添加依賴的方法如何調(diào)用redis,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-06-06
  • SpringBoot使用Spring-Data-Jpa實(shí)現(xiàn)CRUD操作

    SpringBoot使用Spring-Data-Jpa實(shí)現(xiàn)CRUD操作

    這篇文章主要為大家詳細(xì)介紹了SpringBoot使用Spring-Data-Jpa實(shí)現(xiàn)CRUD操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • SpringBoot實(shí)現(xiàn)定時(shí)發(fā)送郵件的三種方法案例詳解

    SpringBoot實(shí)現(xiàn)定時(shí)發(fā)送郵件的三種方法案例詳解

    這篇文章主要介紹了SpringBoot三種方法實(shí)現(xiàn)定時(shí)發(fā)送郵件的案例,Spring框架的定時(shí)任務(wù)調(diào)度功能支持配置和注解兩種方式Spring?Boot在Spring框架的基礎(chǔ)上實(shí)現(xiàn)了繼承,并對(duì)其中基于注解方式的定時(shí)任務(wù)實(shí)現(xiàn)了非常好的支持,本文給大家詳細(xì)講解,需要的朋友可以參考下
    2023-03-03
  • SpringBoot+微信小程序?qū)崿F(xiàn)文件上傳與下載功能詳解

    SpringBoot+微信小程序?qū)崿F(xiàn)文件上傳與下載功能詳解

    這篇文章主要為大家介紹了SpringBoot整合微信小程序?qū)崿F(xiàn)文件上傳與下載功能,文中的實(shí)現(xiàn)步驟講解詳細(xì),快跟隨小編一起學(xué)習(xí)一下吧
    2022-03-03
  • java中SpringBoot?自動(dòng)裝配的原理分析

    java中SpringBoot?自動(dòng)裝配的原理分析

    這篇文章主要介紹了SpringBoot?自動(dòng)裝配的原理分析的相關(guān)資料,需要的朋友可以參考下
    2022-12-12

最新評(píng)論