当前位置: 首页 > news >正文

Spring Security学习笔记(二)Spring Security认证和鉴权

前言:本系列博客基于Spring Boot 2.6.x依赖的Spring Security5.6.x版本

上一篇博客介绍了Spring Security的整体架构,本篇博客要讲的是Spring Security的认证和鉴权两个重要的机制。
在这里插入图片描述
UsernamePasswordAuthenticationFilter和BasicAuthenticationFilter是用来认证的两个过滤器,FilterSecurityInterceptor是用来鉴权的。

一、Spring Security认证

Spring Security提供了许多认证机制,例如用户名密码认证、OAuth 2.0认证、SAML认证、Central Authentication Server (CAS)认证、Remember Me(记住过了session有效期的用户)、JAAS认证、X509认证等

1.1、认证架构

Spring Security认证架构主要由以下几个组件构成:
SecurityContext:Spring Security的上下文对象,包含了当前认证用户的Authentication(认证)。

SecurityContextHolder:用于设置和获取SecurityContext的静态工具类,保存了SecurityContext上下文对象。

Authentication:认证接口,定义了获取用户凭证、认证信息、权限等方法规范。

GrantedAuthority:权限类,用来定义用户的权限,Authentication中会保存一个GrantedAuthority类型的权限列表。

AuthenticationManager:认证管理器接口,只有一个authenticate方法,它的实现类实现该方法用来执行具体的认证逻辑,入参和出参都是Authentication。

ProviderManager:最常见的AuthenticationManager的实现。

AuthenticationProvider:认证功能提供者接口。在ProviderManager中实际上的认证逻辑由该接口的实现类处理。DaoAuthenticationProvider、AnonymousAuthenticationProvider都是它的实现类。

AuthenticationEntryPoint:用于从客户端请求凭证(即重定向到登录页面,返回需要登录响应等)。

AbstractAuthenticationProcessingFilter:一个用于认证的基本 Filter。是一个抽象类,只有UsernamePasswordAuthenticationFilter一个实现,UsernamePasswordAuthenticationFilter会从请求中获取username和 password参数,去进行认证。

1.1.1、SecurityContext

Spring Security的上下文对象,可以设置和获取Authentication认证信息。

public interface SecurityContext extends Serializable {// 获取Authentication对象Authentication getAuthentication();// 放入Authentication对象void setAuthentication(Authentication authentication);
}

1.1.2、SecurityContextHolder

SecurityContextHolder是用来设置和获取SecurityContext的静态工具类,SecurityContextHolder不关心SecurityContext里认证信息的细节,即Authentication的具体实现类型是什么它并不关心,如果它能获取到值,这个值就认为是当前用户的认证信息。

public class SecurityContextHolder {...//常用方法public static void clearContext() {strategy.clearContext();}public static SecurityContext getContext() {return strategy.getContext();}public static void setContext(SecurityContext context) {strategy.setContext(context);}...
}

SecurityContextHolder架构图:
在这里插入图片描述
默认情况下,SecurityContextHolder使用ThreadLocal来存储这些细节,这意味着 SecurityContext 对同一线程中的方法总是可用的,即使SecurityContext没有被明确地作为参数传递给这些方法。并且Spring Security的FilterChainProxy会确保SecurityContext总是被清空,不用我们手动清空。

1.1.3、Authentication

Authentication是认证信息接口,定义了获取用户凭证、认证信息、权限等方法规范。它主要有两个作用,一是充当未认证的用户凭证(包括用户名、密码);一是表示验证后的认证信息(包括认证后用户信息、用户权限等)。Authentication一般包含了如下信息:

principal: 识别用户。当用用户名/密码进行认证时,这通常是 UserDetails 的一个实例。

credentials: 通常是一个密码。在许多情况下,这在用户被认证后被清除,以确保它不会被泄露。

authorities: GrantedAuthority 实例是用户被授予的权限。

public interface Authentication extends Principal, Serializable {//获取用户权限,一般情况下获取到的是用户的角色信息Collection<? extends GrantedAuthority> getAuthorities();//获取证明用户认证的信息,通常情况下获取到的是密码等信息Object getCredentials();//获取用户的额外信息,(这部分信息可以是我们的用户表中的信息)Object getDetails();// 获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是 UserDetailsObject getPrincipal();//获取当前 Authentication 是否已认证boolean isAuthenticated();//设置当前 Authentication 是否已认证(true or false)void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

1.1.4、GrantedAuthority

Spring Security定义的权限类规范接口,认证后的用户权限就是以GrantedAuthority类型的集合保存的。使用时分两种权限,分别是角色(role)和作用域(scope)。role类型的权限表示该权限为角色,角色可能会对应许多的具体资源(菜单、接口等)权限;scope表示某个具体资源的权限。一般使用role类型的权限,因为使用scope的话,认证时可能会保存有非常多的GrantedAuthority,容易导致内存不足,而role类型基本没有这种问题。注意设置role类型的权限时,权限最好加上ROLE_ 前缀,Spring Security默认的role类型鉴权方法会有ROLE_ 前缀。

public interface GrantedAuthority extends Serializable {//拿到权限名String getAuthority();
}

1.1.5、AuthenticationManager

认证管理器接口,定义了执行认证逻辑的方法API。常用的实现类为ProviderManager。

public interface AuthenticationManager {//用户执行认证时的方法,具体逻辑由实现类实现Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

1.1.6 、ProviderManager

ProviderManager是最常用的AuthenticationManager的实现。ProviderManager委托给一个 AuthenticationProvider集合。每个 AuthenticationProvider都有机会表明认证应该是成功的、失败的,或者表明它不能做出决定并允许下游的AuthenticationProvider来决定。如果配置的 AuthenticationProvider实例中没有一个能进行认证,那么认证就会以ProviderNotFoundException 而失败,这是一个特殊的AuthenticationException,表明ProviderManager没有被配置为支持被传入它的Authentication类型。
在这里插入图片描述

1.1.7、AuthenticationProvider

实际上执行认证逻辑的地方。常用的实习类DaoAuthenticationProvider(支持基于用户名/密码的认证)、AnonymousAuthenticationProvider(匿名用户认证)

public interface AuthenticationProvider {//执行具体认证逻辑Authentication authenticate(Authentication authentication) throws AuthenticationException;boolean supports(Class<?> authentication);
}

1.1.8、AuthenticationEntryPoint

如果用户访问一个需要认证后才能访问的资源,AuthenticationEntryPoint就会返回一个响应,需要用户先认证后或者携带认证凭证再访问。比如重定向到登录页面,或者返回一个携带“需要登录”提示的响应信息。我们可以实现该接口,自定义的未登录认证提示。Spring Security默认会对未认证去访问需要认证的资源的请求返回403。

1.1.9、AbstractAuthenticationProcessingFilter

用户认证的基础Filter,只有UsernamePasswordAuthenticationFilter这一个实现类。

AbstractAuthenticationProcessingFilter源码:

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {...//主要方法public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {this.doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain);}private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {//先校验请求url与表单校验提交的url是否一致,不一致执行下一个Filter//一致的话就执行认证逻辑,一般默认的表单提交url是"/login"if (!this.requiresAuthentication(request, response)) {chain.doFilter(request, response);} else {try {//实现类执行具体的认证逻辑Authentication authenticationResult = this.attemptAuthentication(request, response);if (authenticationResult == null) {return;}this.sessionStrategy.onAuthentication(authenticationResult, request, response);if (this.continueChainBeforeSuccessfulAuthentication) {chain.doFilter(request, response);}this.successfulAuthentication(request, response, chain, authenticationResult);} catch (InternalAuthenticationServiceException var5) {this.logger.error("An internal error occurred while trying to authenticate the user.", var5);this.unsuccessfulAuthentication(request, response, var5);} catch (AuthenticationException var6) {this.unsuccessfulAuthentication(request, response, var6);}}}//由子类实现具体的验证逻辑public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException;...
}

UsernamePasswordAuthenticationFilter的 attemptAuthentication() 方法

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (this.postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());} else {//取用户名,实际上是从request取username参数String username = this.obtainUsername(request);username = username != null ? username : "";username = username.trim();//取密码,实际上是从request取password参数String password = this.obtainPassword(request);password = password != null ? password : "";UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);this.setDetails(request, authRequest);return this.getAuthenticationManager().authenticate(authRequest);}}

在这里插入图片描述
AbstractAuthenticationProcessingFilter认证步骤:
1、当用户提交他们的凭证(用户名和密码)时,AbstractAuthenticationProcessingFilter会从HttpServletRequest中创建一个要认证的Authentication。创建的认证的类型取决于 AbstractAuthenticationProcessingFilter的子类。例如,UsernamePasswordAuthenticationFilter从HttpServletRequest中提交的username和password创建一个 UsernamePasswordAuthenticationToken。

2、接下来,Authentication被传入AuthenticationManager,执行认证逻辑。

3、如果认证失败,则为Failure。

  • SecurityContextHolder被清空。

  • RememberMeServices.loginFail被调用。如果没有配置记住我(remember me),可以忽略。

  • AuthenticationFailureHandler被调用。参考AuthenticationFailureHandler接口。

4、 如果认证成功,则为Success。

  • SessionAuthenticationStrategy被通知有新的登录。参考SessionAuthenticationStrategy接口。

  • Authentication是在SecurityContextHolder上设置的。如果你需要保存SecurityContext以便在未来的请求中自动设置,必须显式调用SecurityContextRepository#saveContext。参考 SecurityContextHolderFilter类。

  • RememberMeServices.loginSuccess 被调用。如果没有配置remember me,可以忽略。

  • ApplicationEventPublisher发布一个InteractiveAuthenticationSuccessEvent事件。

  • AuthenticationSuccessHandler被调用。参考AuthenticationSuccessHandler接口。

1.2、代码示例

1.2.1、默认登录认证

引入需要用到的相关包。

	<dependencies><!-- 如果你项目的maven父工程是spring-boot-starter-parent包,可以不写版本号,springboot管理了版本号--><!--Spring Security--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency><!--使用undertow容器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency></dependencies>

定义一个controller

@Controller
public class LoginController {//主页url@RequestMapping("/main")public String mainPage(){return "main";}
}

resource/templates/ 路径下里定义一个main.html作为主页

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>主页面</title>
</head>
<body><h1>主页面</h1><form th:action="@{/logout}" method="post"><input type="submit" value="登出"></form>
</body>
</html>

application.yml

server:port: 8084servlet:context-path: /securityspring:security:#配置Spring Security默认登录用户和密码#不配置的话,启动项目时,Spring Security会在控制台打印出默认密码,用户名是Useruser:name: Userpassword: 123456

一切准备就绪,启动项目,访问localhost:8084/security/main,会自动重定向到Spring Security的默认登录页面。
在这里插入图片描述
这是因为Spring Security使用了默认的表单登录认证的方式。查看控制台打印信息,可以看到类似下面的输出。
如果没有,可能是Spring Security的版本问题,我使用的Spring Boot-2.6.2引入的Spring Security-5.6.2,关于这一块的打印信息逻辑写错了,导致未打印,可以将Spring Boot版本升级一下。

2023-06-14T08:55:22.321-03:00  INFO 76975 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [
org.springframework.security.web.session.DisableEncodeUrlFilter@404db674,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@50f097b5,
org.springframework.security.web.context.SecurityContextHolderFilter@6fc6deb7,
org.springframework.security.web.header.HeaderWriterFilter@6f76c2cc,
org.springframework.security.web.csrf.CsrfFilter@c29fe36,
org.springframework.security.web.authentication.logout.LogoutFilter@ef60710,
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@7c2dfa2,
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4397a639,
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@7add838c,
org.springframework.security.web.authentication.www.BasicAuthenticationFilter@5cc9d3d0,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7da39774,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@32b0876c,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@3662bdff,
org.springframework.security.web.access.ExceptionTranslationFilter@77681ce4,
org.springframework.security.web.access.intercept.AuthorizationFilter@169268a7]

UsernamePasswordAuthenticationFilter过滤器就是用来表单登录认证的Filter。

1.2.2、自定义登录页面

resource/templates/ 路径下里定义一个login.html作为登录页

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>登录页面</title>
</head>
<body><h1>登录页面</h1><!--method必须为post--><!--th:action="@{/login}",使用动态参数,表单中会自动生成_csrf隐藏字段,用于防止csrf攻击login:和登录页面保持一致即可,SpringSecurity自动进行登录认证/login 是Spring Security默认的登录认证路径,默认情况下用户名和密码名称必须是username和password--><form th:action="@{/login}"  method="post">用户名:<input type="text" name="username"> <br>密码:<input type="password" name="password"><br><input type="submit"></form>
</body>
</html>

LoginController添加登录页面跳转接口

	@RequestMapping("/myLoginPage")public String myLoginPage(){return "login";}

自定义Spring Security的配置类

@Configuration
public class BasicSecurityConfig {@Beanpublic SecurityFilterChain mySecurityFilterChain(HttpSecurity http) throws Exception {// 登录相关配置http.formLogin(formLogin -> formLogin.loginPage("/myLoginPage") // 自定义登录页面,不再使用内置的自动生成页面//登录认证接口url,这里可以任意设置,只要保证和登录表单提交的url相同即可.loginProcessingUrl("/login").usernameParameter("username")// 表单中的用户名项.passwordParameter("password")// 表单中的密码项.successForwardUrl("/main")//登录成功后跳转的路径,未设置会跳转到项目根路径);//设置访问权限,如果不设置,默认所有的url都可以匿名访问http.authorizeRequests(authorize ->{authorize.antMatchers("/myLoginPage").permitAll() //允许所有用户访问.anyRequest()   //对所有请求开启授权保护.authenticated(); //已认证的请求会被自动授权});http.logout(logout ->logout.logoutUrl("/logout") //使用该方法时,当开启csrf防护,logout请求必须是post,否则会404.clearAuthentication(true) //清除认证状态,默认为true.invalidateHttpSession(true) // 销毁HttpSession对象,默认为true);//关闭csrf防护,否则所有的POST的请求都需要携带CSRF令牌http.csrf(csrf -> csrf.disable());return http.build(); // 返回构建的SecurityFilterChain实例}
}

还有一种写法是继承WebSecurityConfigurerAdapter类,重写configure方法,但是Spring Security 6.0及之后的版本删除了WebSecurityConfigurerAdapter类,不能用这种写法配置了。

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overridepublic void configure(HttpSecurity http) throws Exception {//和上面配置相同,最后无需调用http.build()方法....}
}

注意:使用表单登录认证时,实际处理认证的是UsernamePasswordAuthenticationFilter类,loginProcessingUrl方法配置的url可以任意配置,只要和登录表单提交的url相同即可。

1.2.3、自定义Handler逻辑

Spring Security定义了一些Handler接口,让我们可以自定义认证结束后的处理逻辑。比如返回JSON结果,适用于前后端分离的项目。

1.2.3.1、认证成功处理

AuthenticationSuccessHandler类是Spring Security提供的认证成功后处理逻辑接口。
实现AuthenticationSuccessHandler接口:

public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {//  获取用户身份信息UserDetails userDetails = (UserDetails)authentication.getPrincipal();//  获取用户的凭证信息Object credentials = authentication.getCredentials();//  获取用户权限信息Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();/*返回页面,适用于前后端未分离的项目*/System.out.println("用户名:"+userDetails.getUsername());System.out.println("一些操作...");//response.sendRedirect(request.getContextPath()+"/main");/*返回json,适用于前后端分离*///这里可以生成token,并存redis等Map<String,Object> result = new HashMap();result.put("code",0);   // 成功result.put("message","登录成功");   //result.put("data",userDetails);   //这里可以换成token,jwt等登录成功凭证//  将结果对象转换成json字符串String json = JSON.toJSONString(result);//  返回json数据到前端//  响应头response.setContentType("application/json;charset=UTF-8");//  响应体response.getWriter().println(json);}
}

在BasicSecurityConfig配置类的formLogin中加上MyAuthenticationSuccessHandler

		// 登录相关配置http.formLogin(formLogin -> formLogin.loginPage("/myLoginPage") // 自定义登录页面,不再使用内置的自动生成页面//登录认证接口url,这里可以任意设置,只要保证和登录表单提交的url相同即可.loginProcessingUrl("/login").usernameParameter("username")// 表单中的用户名项.passwordParameter("password")// 表单中的密码项.successForwardUrl("/main")//登录成功后跳转的路径,未设置会跳转到项目根路径.successHandler(new MyAuthenticationSuccessHandler()) //认证成功处理);
1.2.3.2、认证失败处理

AuthenticationFailureHandler类是Spring Security提供的认证失败处理逻辑接口。
实现AuthenticationFailureHandler接口:

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {//  获取失败的信息String localizedMessage = exception.getLocalizedMessage();Map<String,Object> result = new HashMap();result.put("code",-1);   // 失败result.put("message",localizedMessage);   ////  将结果对象转换成json字符串String json = JSON.toJSONString(result);//  返回json数据到前端//  响应头response.setContentType("application/json;charset=UTF-8");//  响应体response.getWriter().println(json);//重定向到登录错误页面,适用与前后端不分离项目//response.sendRedirect(request.getContextPath()+"/loginError");}
}

在BasicSecurityConfig配置类的formLogin中加上MyAuthenticationFailureHandler

		// 登录相关配置http.formLogin(formLogin -> formLogin....successHandler(new MyAuthenticationSuccessHandler()) //认证成功处理.failureHandler(new MyAuthenticationFailureHandler()) //认证失败处理);
1.2.3.3、登出成功处理

LogoutSuccessHandler类是Spring Security提供的登出成功处理逻辑接口。
实现LogoutSuccessHandler接口:

public class MyLogoutSuccessHandler implements LogoutSuccessHandler {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {//  获取用户身份信息UserDetails userDetails = (UserDetails)authentication.getPrincipal();/** 返回json,适用于前后端分离*/Map<String,Object> result = new HashMap();result.put("code",1);   // 成功result.put("message","注销成功");   //result.put("data",userDetails);   ////  将结果对象转换成json字符串String json = JSON.toJSONString(result);//  返回json数据到前端 适用前后端分离//  响应头response.setContentType("application/json;charset=UTF-8");//  响应体response.getWriter().println(json);//返回到页面//response.sendRedirect(request.getContextPath()+"/main");}
}

在BasicSecurityConfig配置类的logout中加上MyLogoutSuccessHandler

http.logout(logout ->logout.logoutUrl("/logout") //使用该方法时,当开启csrf防护,logout请求必须是post,否则会404.clearAuthentication(true) //清除认证状态,默认为true.invalidateHttpSession(true) // 销毁HttpSession对象,默认为true.logoutSuccessHandler(new MyLogoutSuccessHandler()));
1.2.3.4、请求未认证资源处理

AuthenticationEntryPoint类是Spring Security提供的未认证访问资源处理逻辑接口。
实现AuthenticationEntryPoint类:

public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {String localizedMessage = "需要登录";//authException.getLocalizedMessage();Map<String,Object> result = new HashMap();result.put("code",-1);   // 告诉用户需要登录result.put("message",localizedMessage);   ////  将结果对象转换成json字符串String json = JSON.toJSONString(result);//  返回json数据到前端//  响应头response.setContentType("application/json;charset=UTF-8");//  响应体response.getWriter().println(json);//返回登录界面//response.sendRedirect(request.getContextPath()+"/myLoginPage");}
}

在BasicSecurityConfig配置类的中加上配置

		//异常处理http.exceptionHandling(exception -> exception.authenticationEntryPoint(new MyAuthenticationEntryPoint()) //请求未认证的处理);

1.2.4、基于数据库的认证

前面的示例中,我们的登录用户是写在配置文件里的,用的是基于内存存储用户信息的方式。这只能在学习时使用,在实际项目中是不行的。实际项目中,我们的用户信息时存在数据库里的,Spring Security也提供了基于数据库来进行认证的方式。

前文我们已经说过,通过HttpSecurityformLogin方法配置的认证,是使用UsernamePasswordAuthenticationFilter类来进行的认证处理,而实际上处理时,是在ProviderManager的authenticate方法里,再调用DaoAuthenticationProvider的authenticate方法处理的。最终的处理是在DaoAuthenticationProvider类的父类AbstractUserDetailsAuthenticationProvider类的authenticate处理的。

而在进行认证前,需要先根据用户名查询系统里的用户数据(内存或数据库),再根据查询到的用户密码与用户输入的密码校验,校验通过,则认证成功。这一块的逻辑是由DaoAuthenticationProvider类重写父类的retrieveUser实现的。源码如下:

protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {this.prepareTimingAttackProtection();try {//拿到用户信息UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);if (loadedUser == null) {throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");} else {return loadedUser;}} catch (UsernameNotFoundException var4) {this.mitigateAgainstTimingAttack(authentication);throw var4;} catch (InternalAuthenticationServiceException var5) {throw var5;} catch (Exception var6) {throw new InternalAuthenticationServiceException(var6.getMessage(), var6);}}

通过调用UserDetailsService的loadUserByUsername方法,返回系统的用户信息。我们可以通过实现自己的UserDetailsService实现类,重写loadUserByUsername方法,查询数据库里的用户数据。代码如下:


public class DBUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {/*** UserDetails提供的字段如果不够的话,可以继承 User类,实现自己的UserDetails* 用户认证时会调用* @param username* @return* @throws UsernameNotFoundException*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据userName去数据库查询用户信息, 伪代码UserDomain user = userService.queryUserByUserName(username);if(user == null){throw new UsernameNotFoundException(username);}//查询用户的角色,伪代码List<String> roles = roleService.getRoleCodeByUserId(user.getId());UserDetails userDetails = User.withUsername(user.getLoginName()).password(user.getPassword())//.authorities(roles.toArray(new String[roles.size()]))  //权限,和roles配一个就行,这里配置不会加前缀.roles(roles.toArray(new String[roles.size()])) //角色 配置角色时,会给资源自动加上ROLE_前缀.build();return userDetails;}@Overridepublic void createUser(UserDetails user) {}@Overridepublic void updateUser(UserDetails user) {}@Overridepublic void deleteUser(String username) {}@Overridepublic void changePassword(String oldPassword, String newPassword) {}@Overridepublic boolean userExists(String username) {return false;}@Overridepublic UserDetails updatePassword(UserDetails user, String newPassword) {return null;}
}

然后在配置类中加上相关配置:

@Configuration
public class BasicSecurityConfig {.../*** 密码编码器,会对请求传入的密码进行加密* @return*/@Beanpublic PasswordEncoder passwordEncoder() {//return NoOpPasswordEncoder.getInstance();return new BCryptPasswordEncoder();}@Beanpublic UserDetailsService userDetailsService(){return new DBUserDetailsManager();}...
}	

需要加一个密码编码器,使用Spring Security提供的默认编码器就行,使用编码器后,注意数据库保存的密码应该是密文。直接将我们的UserDetailsService注入到Spring容器中即可生效。

二、Spring Security鉴权

2.1、鉴权架构

2.1.1、FilterSecurityInterceptor

Spring Security进行鉴权处理的入口。父类是AbstractSecurityInterceptor类

2.1.2、AccessDecisionManager

Spring Security鉴权的真正处理者

public interface AccessDecisionManager {//鉴权方法 /*** authentication 当前用户的认证凭证信息,包括了用户信息,权限等* object 一般是FilterInvocation,包含了当前请求的request和response* configAttributes过滤规则,由配置类里的 HttpSecurity的authorizeRequests方法配置*/void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException;//是否允许AccessDecisionManager处理该过滤规则,true为允许boolean supports(ConfigAttribute attribute);//是否允许AccessDecisionManager处理clazz类型,true为允许boolean supports(Class<?> clazz);
}

Spring Security的鉴权是基于投票机制的鉴权方式。
在这里插入图片描述

2.1.3、AccessDecisionVoter

投票器,AccessDecisionManager的投票处理是由AccessDecisionVoter投票器决定的,一个AccessDecisionManager里会包含一个AccessDecisionVoter集合,AccessDecisionManager会根据所有投票器的投票结果来决定请求是否有权访问,无权限会抛出一个 AccessDeniedException。

public interface AccessDecisionVoter<S> {//同意int ACCESS_GRANTED = 1;//弃权int ACCESS_ABSTAIN = 0;//反对int ACCESS_DENIED = -1;boolean supports(ConfigAttribute attribute);boolean supports(Class<?> clazz);//投票方法int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
}

AccessDecisionManager有三个实现类

  • AffirmativeBased:一票通过,只要有一票通过就算通过,默认是它。
  • UnanimousBased:一票反对,只要有一票反对就不能通过。
  • ConsensusBased:少数票服从多数票。

2.2、代码示例

2.2.1、默认鉴权

定义两个接口,分别由两种权限访问。在LoginController中新增

	//admin权限@RequestMapping("/adminRole")@ResponseBodypublic String adminRole(){return "success";}//tourist权限@RequestMapping("/touristRole")@ResponseBodypublic String touristRole(){return "success";}

在BasicSecurityConfig配置类中新增这两个接口鉴权配置:

	@Beanpublic SecurityFilterChain mySecurityFilterChain(HttpSecurity http) throws Exception {//和前文一样的配置省略了...//设置访问权限,如果不设置,默认所有的url都可以匿名访问http.authorizeRequests(authorize ->{authorize// 放行所有OPTIONS请求.antMatchers(HttpMethod.OPTIONS).permitAll().antMatchers("/myLoginPage").permitAll() //登录页面允许所有用户访问.antMatchers("/adminRole").hasRole("AdminManager")	// /adminRole 只能AdminManager角色访问.antMatchers("/touristRole").hasAnyRole("AdminManager","ApproveUser")	// /touristRole AdminManager和ApproveUser角色都能访问.anyRequest()   //对所有请求开启授权保护.authenticated(); //已认证的请求会被自动授权});...return http.build(); // 返回构建的SecurityFilterChain实例}

通过给“/adminRole”和"/touristRole"接口配置权限过滤规则,用户访问接口时,就会在登录认证成功后,在SecurityContext上下文中设置凭证信息,其中就包括当前用户的权限,然后匹配配置的权限过滤规则,判断当前用户是否有该接口的权限。如果不配置权限过滤规则,则默认认证成功的用户都可以访问。

前文说过,在进行表单登录认证时,Spring Security是通过调用UserDetailsService的loadUserByUsername方法,得到当前登录用户的信息的,其中就包括权限信息。

 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据userName去数据库查询用户信息, 伪代码UserDomain user = userService.queryUserByUserName(username);if(user == null){throw new UsernameNotFoundException(username);}//查询用户的角色,伪代码List<String> roles = roleService.getRoleCodeByUserId(user.getId());UserDetails userDetails = User.withUsername(user.getLoginName()).password(user.getPassword())//.authorities(roles.toArray(new String[roles.size()]))  //权限,和roles配一个就行,这里配置不会加前缀.roles(roles.toArray(new String[roles.size()])) //角色 配置角色时,会给资源自动加上ROLE_前缀.build();return userDetails;}

通过Spring Security的User类的roles和authorities方法,就可以设置当前登录用户的权限信息。这里需要注意的是,如果配置权限过滤规则时,使用的是role(角色)权限,loadUserByUsername方法也得设置role权限,反之亦然。权限名称相同即可。

2.2.2、请求未授权接口处理

Spring Security定义了AccessDeniedHandler接口,用来处理访问未授权接口的请求。只需实现AccessDeniedHandler接口,然后将自定义的类加入到配置里即可。

public class MyAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {Map<String,Object> result = new HashMap();result.put("code",-1);   // 没有权限result.put("message","没有权限");   ////  将结果对象转换成json字符串String json = JSON.toJSONString(result);//  返回json数据到前端//  响应头response.setContentType("application/json;charset=UTF-8");//  响应体response.getWriter().println(json);//返回页面//response.sendRedirect(request.getContextPath()+"/main");}
}

在BasicSecurityConfig配置类中加上该类

	@Beanpublic SecurityFilterChain mySecurityFilterChain(HttpSecurity http) throws Exception {...//异常处理http.exceptionHandling(exception -> exception.authenticationEntryPoint(new MyAuthenticationEntryPoint()) //请求未认证的处理.accessDeniedHandler(new MyAccessDeniedHandler())   //未授权处理);//关闭csrf防护,否则所有的POST的请求都需要携带CSRF令牌http.csrf(csrf -> csrf.disable());return http.build(); // 返回构建的SecurityFilterChain实例}

2.2.3、基于方法注解的方式鉴权

Spring Security提供了基于注解的方式,设置权限过滤规则的方法。具体使用如下:
使用@EnableMethodSecurity注解开启基于方法的授权,在自定义的BasicSecurityConfig配置类上加上即可

@Configuration
@EnableMethodSecurity
public class BasicSecurityConfig {...
}

然后在Controller的方法上使用@PreAuthorize注解即可。首先在配置类里去掉"/adminRole"和"/touristRole"的权限过滤规则配置。然后在LoginController里给这两个接口加上@PreAuthorize注解:

	@RequestMapping("/adminRole")@ResponseBody@PreAuthorize("hasAnyRole('AdminManager')")public String adminRole(){return "success";}@RequestMapping("/touristRole")@ResponseBody@PreAuthorize("hasAnyRole('AdminManager','ApproveUser')")public String touristRole(){return "success";}

@PreAuthorize里可以使用SpEL表达式,例如:hasRole(‘ADMIN’) and authentication.name == ‘User’ 这种。可以使用的规则如下:
在这里插入图片描述
具体可以参考Spring Security关于这一块的官网介绍:

https://springdoc.cn/spring-security/servlet/authorization/authorize-http-requests.html#authorization-expressions

相类似的注解还有@PostAuthorize、@PreFilter、@PostFilter等。具体的用法也可以去官网查找。

相关文章:

Spring Security学习笔记(二)Spring Security认证和鉴权

前言&#xff1a;本系列博客基于Spring Boot 2.6.x依赖的Spring Security5.6.x版本 上一篇博客介绍了Spring Security的整体架构&#xff0c;本篇博客要讲的是Spring Security的认证和鉴权两个重要的机制。 UsernamePasswordAuthenticationFilter和BasicAuthenticationFilter是…...

产品经理NPDP好考吗?

NPDP是新产品开发专业人员的资格认证&#xff0c;对于希望在产品管理领域取得认可的专业人士来说&#xff0c;NPDP认证是一项重要的资格。 那么&#xff0c;产品经理考取NPDP资格认证究竟难不难呢&#xff1f; 首先&#xff0c;NPDP考试的难易程度取决于考生的背景和准备情况…...

【C++】:红黑树的应用 --- 封装map和set

点击跳转至文章&#xff1a;【C】&#xff1a;红黑树深度剖析 — 手撕红黑树&#xff01; 目录 前言一&#xff0c;红黑树的改造1. 红黑树的主体框架2. 对红黑树节点结构的改造3. 红黑树的迭代器3.1 迭代器类3.2 Begin() 和 End() 四&#xff0c;红黑树相关接口的改造4.1 Find…...

unity美术资源优化(资源冗余,主界面图集过多)

图片资源冗余&#xff1a; UPR unity的性能优化工具检查资源 1.检查纹理读/写标记 开启纹理资源的读/写标志会导致双倍的内存占用 检查Inspector -> Advanced -> Read/Write Enabled选项 2.检查纹理资源alpha通道 如果纹理的alpha通道全部为0&#xff0c;或者全部为2…...

【git】github中的Pull Request是什么

在 Git 中&#xff0c;"pull request"&#xff08;简称 PR&#xff09;是一种在分布式版本控制系统中使用的功能&#xff0c;特别是在使用 GitHub、GitLab、Bitbucket 等基于 Git 的代码托管平台时。Pull Request 允许开发者请求将他们的代码更改合并到另一个分支&am…...

gitlab查询分支API显示不全,只有20个问题

背景 gitlab查询分支API需要查询所有分支&#xff0c;且分支数量大于20&#xff0c;但目前调用接口返回的branch最多就显示了20个 解决方案 根据GitLab的文档&#xff0c;查询分支API默认最多返回20个分支。如果要一次性显示80个分支&#xff0c;可以使用分页参数来获取所有…...

vue3+vite 实现动态引入某个文件夹下的组件 - glob-import的使用

<template><div class"user-content"><HeaderTitle title"用户详情"></HeaderTitle><div class"main-content"><div><UserForm /></div><div><TableList></TableList></d…...

hhhhh

x torch.tensor([1.0,0.],[-1.,1.],requires_gradTrue) z x.pow(2).sum() z.backward() x.grad在这段代码中&#xff0c;我们利用 PyTorch 进行自动求梯度&#xff0c;下面详细解释代码的每一个部分及其在反向传播中的作用。同时&#xff0c;我们也将介绍函数对象和叶子节点的…...

扫雷小游戏纯后端版

package com.wind;import java.util.Random; import java.util.Scanner;public class ResultLei {static Random random new Random();public static void main(String[] args) {boolean end true;while (end) {System.out.println("请输入你选择的难度对应的数字&#…...

RuoYi-Vue-Plus(动态添加移除数据源)

一、添加数据 private final DynamicRoutingDataSource dynamicRoutingDataSource;private final DefaultDataSourceCreator dataSourceCreator;//添加一个dynamic的数据源@GetMapping("createDynamic")public void createDynamic() {DataSourceProperty property =…...

idea启动项目报:the command line via JAR manifest or via a classpath file and rerun.

解决方案 1.打开Edit Configurations&#xff0c;进去编辑&#xff0c;如下&#xff1a; 笔记配置 2.选择Modfiy options,点击Shorten command line 3.在新增的Shorten command line选项中选择JAR manifest或classpath file 4.点击保存后即可...

vue3 + ts中有哪些类型是由vue3提供的?

在 Vue 3 中结合 TypeScript 使用时&#xff0c;Vue 提供了一系列的类型帮助函数和接口&#xff0c;这些类型用于增强 TypeScript 的集成和提供类型安全。以下是一些由 Vue 3 提供的常用 TypeScript 类型&#xff1a; RefType: 用于标注一个 ref 返回的响应式引用类型。Reacti…...

【Linux】远程连接Linux虚拟机(MobaXterm)

【Linux】远程连接Linux虚拟机&#xff08;MobaXterm&#xff09; 零、原因 有时候我们在虚拟机中操作Linux不太方便&#xff0c;比如不能复制粘贴&#xff0c;不能传文件等等&#xff0c;我们在主机上使用远程连接软件远程连接Linux虚拟机后可以解决上面的问题。 壹、软件下…...

LeetCode Hot100 生成特殊数字的最少操作

给你一个下标从 0 开始的字符串 num &#xff0c;表示一个非负整数。 在一次操作中&#xff0c;您可以选择 num 的任意一位数字并将其删除。请注意&#xff0c;如果你删除 num 中的所有数字&#xff0c;则 num 变为 0。 返回最少需要多少次操作可以使 num 变成特殊数字。 如…...

Spring MVC 应用分层

1. 类名使⽤⼤驼峰⻛格&#xff0c;但以下情形例外&#xff1a;DO/BO/DTO/VO/AO 2. ⽅法名、参数名、成员变量、局部变量统⼀使⽤⼩驼峰⻛格 3. 包名统⼀使⽤⼩写&#xff0c;点分隔符之间有且仅有⼀个⾃然语义的英语单词. 常⻅命名命名⻛格介绍 ⼤驼峰: 所有单词⾸字⺟…...

QT--进程

一、进程QProcess QProcess 用于启动和控制外部进程&#xff0c;管理其输入输出流。 使用方法 start()&#xff1a;启动一个新进程。setStandardInputFile()&#xff1a;将文件作为标准输入。将进程的标准输入&#xff08;stdin&#xff09;重定向到指定的文件。换句话说&am…...

凸优化笔记-基本概念

原文 文章目录 最小二乘问题 仿射affine hullaffine dimension 凸集锥集超平面和半空间单纯形整半定锥保凸性的操作透视函数 凸函数的条件1阶判定条件2阶判定条件 Epigraph 外图 m i n i m i z e f 0 ( x ) minimize\ \ \ f_0(x) minimize f0​(x) s u b j e c t t o f i ( …...

1858. 数组查找及替换

问题描述 给定某整数数组和某一整数 b 。 要求删除数组中可以被 b 整除的所有元素&#xff0c;同时将该数组各元素按从小到大排序。如果数组元素数值在 &#x1d434;‘ 到 Z 的 ASCII 之间&#xff0c;替换为对应字母。 元素个数不超过 100&#xff0c;&#x1d44f; 在 1 …...

计算机视觉与面部识别:技术、应用与未来发展

引言 在当今数字化时代&#xff0c;计算机视觉技术迅速发展&#xff0c;成为人工智能领域的一个重要分支。计算机视觉旨在让机器理解和解释视觉信息&#xff0c;模拟人类的视觉系统。它在各行各业中发挥着重要作用&#xff0c;从自动驾驶汽车到智能监控系统&#xff0c;再到医疗…...

懒人精灵安卓版纯本地离线文字识别插件

目的 懒人精灵是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务&#xff0c;节省大量人工操作的时间。懒人精灵也包含图色功能&#xff0c;识别屏幕上的图像&#xff0c;根据图像的变化自动执行相应的操作。本篇文章主要讲解下更优秀的…...

在线教育数仓项目(数据采集部分1)

文章目录 数据仓库概念项目需求及架构设计项目需求分析系统数据流程设计框架版本选型集群规模估算集群资源规划设计 数据生成模块目标数据页面事件曝光启动播放错误 数据埋点主流埋点方式&#xff08;了解&#xff09;埋点数据上报时机埋点数据日志结构 服务器和JDK准备服务器准…...

帕金森病(PD)诊断:三种基于语音的深度学习方法

帕金森病&#xff08;Parkinson’s disease, PD&#xff09;是世界上第二大流行的神经退行性疾病&#xff0c;全球影响着超过1000万人&#xff0c;仅次于阿尔茨海默症。人们通常在65岁左右被诊断出患有此病。PD的一些症状包括震颤、肌肉僵硬和运动迟缓。这些症状往往出现在较晚…...

【资料分享】2024钉钉杯大数据挑战赛A题思路解析+代码演示

2024第三届钉钉杯大学生大数据挑战赛今天已经开赛&#xff0c;【A题】思路解析代码&#xff0c;资料预览&#xff1a;...

【优质精选】12节大模型系列教学课程之二:RAG 原理与应用

课程二&#xff1a;RAG 原理与应用 12节大模型系列教学课程之二&#xff1a;RAG 原理与应用 课程详细内容RAG 技术的基础知识RAG 的工作原理RAG 提高生成质量和准确性的原理RAG 在问答系统中的应用RAG 在文本创作中的应用RAG 在其他领域的应用探索RAG 技术的挑战与应对策略RAG …...

vue3前端开发-小兔鲜项目-产品详情基础数据渲染

vue3前端开发-小兔鲜项目-产品详情基础数据渲染&#xff01;这一次内容比较多&#xff0c;我们分开写。第一步先完成详情页面的基础数据的渲染。然后再去做一下右侧的热门产品的列表内容。 第一步&#xff0c;还是老规矩&#xff0c;先准备好接口函数。方便我们的页面组件拿到对…...

Docker入门指南:Linux系统下的完整安装步骤与常见问题解答

本文以centos7演示。 Docker安装 可参考官方安装文档&#xff1a;Install Docker Engine on CentOS | Docker Docs 一图流&#xff1a; # 移除旧版本docker sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logro…...

Netty实现数据上下行

Netty实现数据上下行 使用LVSNGinxNetty实现数据接入 在数据上行的时候&#xff0c;通过使用车辆唯一标识码&#xff08;vin&#xff09;和连接通道绑定 Netty一些配置参数如下: #netty项目使用的端口 server.port8017 #使用启用epoll&#xff08;在Linux上拥有更好的传输性…...

【React】事件绑定:深入解析高效处理用户交互的最佳实践

文章目录 一、什么是事件绑定&#xff1f;二、基本事件绑定三、绑定 this 上下文四、传递参数五、事件对象六、事件委托七、常见事件处理八、优化事件处理 React 是现代前端开发中最受欢迎的框架之一&#xff0c;其组件化和高效的状态管理能力使得构建复杂的用户界面变得更加容…...

SpringCloud:使用OpenFeign优化前面的Nacos实现高效购物车商品信息处理

在现代电商系统中&#xff0c;购物车的性能直接影响用户的购物体验。为了提升系统性能和用户满意度&#xff0c;我们可以使用Spring Cloud的OpenFeign和负载均衡器来高效地处理购物车中的商品信息。本文将详细介绍如何在Spring Cloud中集成这些组件&#xff0c;并实现一个高效的…...

计算机三级嵌入式笔记(二)——嵌入式处理器

目录 考点1 嵌入式处理器的结构类型 考点2 嵌入式处理器简介 考点3 ARM处理器概述 考点4 处理器和处理器核 考点5 ARM 处理器的分类 考点6 经典 ARM 处理器 考点7 ARM Cortex 嵌入式处理器 考点8 ARM Cortex实时嵌入式处理器 考点9 ARM Cortex 应用处理器 考点10 AR…...

【GoLang】Golang 快速入门(第一篇)

目录 1.简介&#xff1a; 2.设计初衷&#xff1a; 3.Go语言的 特点 4.应用领域: 5.用go语言的公司&#xff1a; 6. 开发工具介绍以及环境搭建 1.工具介绍: 2.VSCode的安装: 3.安装过程&#xff1a; 4.Windows下搭建Go开发环境--安装和配置SDK 1.搭建Go开发环境 - 安装…...

Linux中的三类读写函数

文件IO和标准IO的区别 遵循标准&#xff1a; 文件IO遵循POSIX标准&#xff0c;主要在类UNIX环境下使用。标准IO遵循ANSI标准&#xff0c;具有更好的可移植性&#xff0c;可以在不同的操作系统上重新编译后运行。可移植性&#xff1a; 文件IO的可移植性相对较差&#xff0c;因为…...

MATLAB基础应用精讲-【数模应用】二元Probit回归分析

目录 前言 知识储备 二元Logistic模型和Probit模型 Logistic模型的形式 Probit模型 优势比(OR) 准二项分布族 算法原理 数学模型 二元因变量和线性概率模型 probit和logit回归 logit和probit模型的估计和推断 稳健性检验 二元logit回归分析全流程 一、案例数…...

找工作准备刷题Day10 回溯算法 (卡尔41期训练营 7.24)

回溯算法今天这几个题目做过&#xff0c;晚上有面试&#xff0c;今天水一水。 第一题&#xff1a;Leetcode77. 组合 题目描述 解题思路 从题目示例来看&#xff0c;k个数是不能重合的&#xff0c;但是题目没有明确说明这一点。 使用回溯算法解决此问题&#xff0c;利用树形…...

如何有效的进行小程序的优化

如今小程序已经成为了许多开发者开展业务&#xff0c;提供服务的重要平台 。所以如何有效的优化小程序成为了开发者关注的首要问题&#xff0c;以下是一份详细的小程序优化方案&#xff1a; 一、目标设定 明确小程序优化的主要目标&#xff0c;例如提高用户留存率、增加用户活…...

FPGA-ROM IP核的使用(2)

前言 接着昨天的进行一个小的实验验证ROM IP核。 实验效果 读取上一期生成的IP核中的数据&#xff0c;并将其显示在数码管上。 具体流程 ROM IP核存放数据0~255&#xff0c;之后每隔0.2s&#xff0c;从0的地址开始读数据&#xff0c;并显示在数码管上&#xff1b;接着先后…...

Manticore Search(es轻量级替代)

概念&#xff1a; Manticore Search 是一个使用 C 开发的高性能搜索引擎&#xff0c;创建于 2017 年&#xff0c;其前身是 Sphinx Search 。Manticore Search 充分利用了 Sphinx&#xff0c;显着改进了它的功能&#xff0c;修复了数百个错误&#xff0c;几乎完全重写了代码并保…...

测试开发面试题---计算机网络

计算机网络模型 OSI模型&#xff1a;七层模型 物理层&#xff1a;定义电气特征&#xff0c;机械特征等功能规范&#xff0c;传递实际比特流数据链路层&#xff1a;物理地址寻址&#xff08;MAC&#xff09;&#xff0c;帧的传输&#xff0c;错误检测和纠正网络层&#xff1a;…...

Wonder3D 论文学习

论文链接&#xff1a;https://arxiv.org/abs/2310.15008 代码链接&#xff1a;https://github.com/xxlong0/Wonder3D 解决了什么问题&#xff1f; 随着扩散模型的提出&#xff0c;3D 生成领域取得了长足进步。从单张图片重建出 3D 几何是计算机图形学和 3D 视觉的基础任务&am…...

【MySQL进阶之路 | 高级篇】显式事务和隐式事务

使用事务有两种方式&#xff1a;显式事务和隐式事务。 1. 显式事务 步骤1&#xff1a; START TRANSACTION或者BEGIN&#xff0c;作用是显式开启一个事务。 START TRANSACTION语句相较于BEGIN特别之处在于&#xff0c;后面能跟几个修饰符。比如&#xff1a; READ ONLY&…...

Ruby、Python、Java 开发者必备:Codigger之软件项目体检

在编程的广阔天地里&#xff0c;Ruby、Python 和 Java 开发者们各自凭借着独特的语言特性&#xff0c;构建着精彩纷呈的应用世界。然而&#xff0c;无论使用哪种语言&#xff0c;确保项目的高质量始终是至关重要的目标。而 Codigger 项目体检则成为了实现这一目标的得力助手&am…...

day05 Router、vuex、axios

配置 router和vuex需要在创建vue项目的时候&#xff0c;开始的时候选择Manually select features&#xff0c;于是就可以在下一个创建配置讯问中选择router和vuex。 axios则需要执行命令行&#xff1a; npm install axios -S 之后再在需要发送请求的view导入即可。 router…...

yolov5-7在opencv里跑自己的onnx模型

先把模型放在如下目录 运行如下代码 import cv2 import numpy as npclass Onnx_clf:def __init__(self, onnx:strdnn_model1/plane02.onnx, img_size640, classlist:list[plane]) -> None: func: 读取onnx模型,并进行目标识别para onnx:模型路径img_size:输出图片大小,和模…...

JVM 11 的优化指南:如何进行JVM调优,JVM调优参数有哪些

这篇文章将详细介绍如何进行JVM 11调优&#xff0c;包括JVM 11调优参数及其应用。此外&#xff0c;我将提供12个实用的代码示例&#xff0c;每个示例都会结合JVM启动参数和Java代码。 本文已收录于&#xff0c;我的技术网站 java-broke.site&#xff0c;有大厂完整面经&#x…...

nginx的配置和使用

一、nginx支持win和linux版本的下载&#xff0c;选择合适的版本进行安装 二、配置文件注解 重点的几个参数进行注释&#xff1a; 1、listen 要监听的服务的端口&#xff0c;符合这个端口的才会被监听 server_name要监听的服务地址&#xff0c;可能是ip,也可能是域名&#xf…...

mysql面试(六)

前言 本章节详细讲解了一下mysql执行计划相关的属性释义&#xff0c;以及不同sql所出现的不同效果 执行计划 一条查询语句经过mysql查询优化器的各种基于成本和各种规则优化之后&#xff0c;会生成一个所谓的 执行计划&#xff0c;这个执行计划展示了这条查询语句具体查询方…...

6.乳腺癌良性恶性预测(二分类、逻辑回归、PCA降维、SVD奇异值分解)

乳腺癌良性恶性预测 1. 特征工程1.1 特征筛选1.2 特征降维 PCA1.3 SVD奇异值分解 2. 代码2.1 逻辑回归、二分类问题2.2 特征降维 PCA2.3 SVD奇异值分解 1. 特征工程 专业上&#xff1a;30个人特征来自于临床一线专家&#xff0c;每个特征和都有医学内涵&#xff1b;数据上&…...

Vue3响应式高阶用法之markRaw()

Vue3响应式高阶用法之markRaw() 文章目录 Vue3响应式高阶用法之markRaw()一、简介二、使用场景2.1 避免性能开销2.2 防止意外修改 三、基本使用3.1 标记对象 四、功能详解4.1 markRaw与reactive的区别4.2 markRaw与ref的区别 五、最佳实践及案例5.1 使用大型第三方库对象5.2 静…...

免费SSL证书的安全性与获取指南

SSL证书是一种数字凭证&#xff0c;用于加密用户与网站之间的信息交换&#xff0c;以确保传输的数据不被第三方窃取。它像是一个数字版的密封印章&#xff0c;为数据的传输过程提供了一层保护膜。 免费的SSL证书通常由CA机构提供&#xff0c;它们同样可以提供基础数据的加密服…...

【CN】Argo 持续集成和交付(一)

1.简介 Argo 英 [ˈɑ:ɡəu] 美 [ˈɑrˌɡo] Kubernetes 原生工具&#xff0c;用于运行工作流程、管理集群以及正确执行 GitOps。 Argo 于 2020 年 3 月 26 日被 CNCF 接受为孵化成熟度级别&#xff0c;然后于 2022 年 12 月 6 日转移到毕业成熟度级别。 argoproj.github.i…...