前端带你学后端系列 ⑥【安全框架Spring Security篇二】

2023-12-18 18:20

本文主要是介绍前端带你学后端系列 ⑥【安全框架Spring Security篇二】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前端带你学后端系列 ⑥【安全框架Spring Security篇二】

  • Ⅰ Spring Security实战一
    • ① Spring Security中的密码加密
    • ② Spring Security四种权限控制方式
    • ③ 关于JWT,以及Spring Security 结合JWT实现登陆验证
      • ① jwt 的组成
      • ② Spring Security 结合JWT登陆验证的流程
        • ① 提前准备,写一个Result返回结果集
        • ② 提前准备,写一个JWT工具类
        • ③ 写LoginSuccessHandler、LoginFailureHandler
        • ④ 验证码相关的配置
          • ① 验证码配置类
          • ② 验证码的controller,返回给前端验证码图片
          • ③ 验证码的filter
        • ⑤ 继承BasicAuthenticationFilter,实现用户验证
        • ⑥ 认证失败的JwtAuthenticationEntryPoint(用户未登录处理类)
        • ⑦ 暂无权限处理类(AccessDeniedHandler,状态码403)
        • ⑧ 登出处理器LogoutSuccessHandler
        • ⑨ 自定义AccountUser类实现UserDetails(拓展原有的UserDetails)
        • ⑩ 实现UserDetailsService,用于和数据库比对
        • ⑩① 密码的加密解密
        • ⑩② SecurityConfig配置类
    • ④ 回顾一下Security的登陆流程

Ⅰ Spring Security实战一

① Spring Security中的密码加密


Spring Security处理密码加密的几种方式
在这里插入图片描述

官方推荐使用BCryptPasswordEncoder

使用方法:

  1. 配置密码加密的方式
@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()//对跨域请求伪造进行防护---->csrf:利用用户带有登录状态的cookie进行攻击的手段.csrf().disable();}//配置采用哪种密码加密算法@Beanpublic PasswordEncoder passwordEncoder() {//不使用密码加密//return NoOpPasswordEncoder.getInstance();//使用默认的BCryptPasswordEncoder加密方案return new BCryptPasswordEncoder();//strength=10,即密钥的迭代次数(strength取值在4~31之间,默认为10)//return new BCryptPasswordEncoder(10);//利用工厂类PasswordEncoderFactories实现,工厂类内部采用的是委派密码编码方案.//return PasswordEncoderFactories.createDelegatingPasswordEncoder();}}
  1. 使用
    //对密码进行加密user.setPassword(passwordEncoder.encode(user.getPassword()));

② Spring Security四种权限控制方式

Spring Security 的认证方式有 认证+授权。我们授权的时候,不仅可以使用默认的授权,还可以自定义授权。

在这里插入图片描、述

使用说明:

  1. 利用Ant表达式(主要是在配置类中SecurityConfig中使用)
    在这里插入图片描述
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/user/**").hasRole("USER").antMatchers("/visitor/**").permitAll().anyRequest().authenticated().and().formLogin().permitAll().and()//对跨域请求伪造进行防护---->csrf:利用用户带有登录状态的cookie进行攻击的手段.csrf().disable();
}
  1. 利用授权注解结合SpEl表达式实现权限控制
    在这里插入图片描述
@RestController
public class UserController {@Secured({"ROLE_USER"})//@PreAuthorize("principal.username.equals('user')")@GetMapping("/user/hello")public String helloUser() {return "hello, user";}@PreAuthorize("hasRole('ADMIN')")@GetMapping("/admin/hello")public String helloAdmin() {return "hello, admin";}@PreAuthorize("#age>100")@GetMapping("/age")public String getAge(@RequestParam("age") Integer age) {return String.valueOf(age);}@GetMapping("/visitor/hello")public String helloVisitor() {return "hello, visitor";}}
  1. 利用过滤器注解实现权限控制
    在这里插入图片描述
@RestController
public class FilterController {/*** 只返回结果中id为偶数的user元素。* filterObject是@PreFilter和@PostFilter中的一个内置表达式,表示集合中的当前对象。*/@PostFilter("filterObject.id%2==0")@GetMapping("/users")public List<User> getAllUser() {List<User> users = new ArrayList<>();for (int i = 0; i < 10; i++) {users.add(new User(i, "yyg-" + i));}return users;}}
  1. 利用动态权限实现权限控制

我们一般会使用标准的RABC进行权限控制,Spring Security中的动态权限,主要是通过重写拦截器和决策器来进行实现,一般满足不了我们的需求。

③ 关于JWT,以及Spring Security 结合JWT实现登陆验证

① jwt 的组成

在这里插入图片描述

② Spring Security 结合JWT登陆验证的流程

在这里插入图片描述

① 提前准备,写一个Result返回结果集
@Data
public class Result implements Serializable {private int code;private String msg;private Object data;public static Result succ(Object data) {return succ(200, "操作成功", data);}public static Result fail(String msg) {return fail(400, msg, null);}public static Result succ (int code, String msg, Object data) {Result result = new Result();result.setCode(code);result.setMsg(msg);result.setData(data);return result;}public static Result fail (int code, String msg, Object data) {Result result = new Result();result.setCode(code);result.setMsg(msg);result.setData(data);return result;}
}
② 提前准备,写一个JWT工具类

该工具类需要有3个功能:生成JWT解析JWT判断JWT是否过期

@Data
@Component
@ConfigurationProperties(prefix = "test.jwt")
public class JwtUtils {private long expire;private String secret;private String header;// 生成JWTpublic String generateToken(String username) {Date nowDate = new Date();Date expireDate = new Date(nowDate.getTime() + 1000 * expire);return Jwts.builder().setHeaderParam("type", "JWT").setSubject(username).setIssuedAt(nowDate).setExpiration(expireDate)    // 7天过期.signWith(SignatureAlgorithm.HS512, secret).compact();}// 解析JWTpublic Claims getClaimsByToken(String jwt) {try {return Jwts.parser().setSigningKey(secret).parseClaimsJws(jwt).getBody();} catch (Exception e) {return null;}}// 判断JWT是否过期public boolean isTokenExpired(Claims claims) {return claims.getExpiration().before(new Date());}}

我们可以配置JWT的有效时间和加密算法所需使用的秘钥,以及返回给前端时在Http response的Header中所叫的名字。这种配置项我们需写入application.yml中,然后使用@ConfigurationProperties注解接收,这样能便于我们日后修改配置。
使用@ConfigurationProperties注解可以读取配置文件中的信息,只要在 Bean 上添加上了这个注解,指定好配置文件中的前缀,那么对应的配置文件数据就会自动填充到 Bean 的属性中
application.yml中的配置如下:

test:jwt:header: Authorizationexpire: 604800 # 7天,s为单位secret: test
③ 写LoginSuccessHandler、LoginFailureHandler

因为我们是前后端分离模式,当成功或者失败以后,需要返回JSON 所以需要写这两个handler。用于成功或者失败的返回

@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {@AutowiredJwtUtils jwtUtils;@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {httpServletResponse.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = httpServletResponse.getOutputStream();// 生成JWT,并放置到请求头中String jwt = jwtUtils.generateToken(authentication.getName());httpServletResponse.setHeader(jwtUtils.getHeader(), jwt);Result result = Result.succ("SuccessLogin");outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {httpServletResponse.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = httpServletResponse.getOutputStream();String errorMessage = "用户名或密码错误";Result result;if (e instanceof CaptchaException) {errorMessage = "验证码错误";result = Result.fail(errorMessage);} else {result = Result.fail(errorMessage);}outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}
④ 验证码相关的配置
① 验证码配置类
/*配置验证码的大小,宽度等等
*/
@Configuration
public class KaptchaConfig {@BeanDefaultKaptcha producer() {Properties properties = new Properties();properties.put("kaptcha.border", "no");properties.put("kaptcha.textproducer.font.color", "black");properties.put("kaptcha.textproducer.char.space", "4");properties.put("kaptcha.image.height", "40");properties.put("kaptcha.image.width", "120");properties.put("kaptcha.textproducer.font.size", "30");Config config = new Config(properties);DefaultKaptcha defaultKaptcha = new DefaultKaptcha();defaultKaptcha.setConfig(config);return defaultKaptcha;}}
② 验证码的controller,返回给前端验证码图片
@GetMapping("/captcha")
public Result Captcha() throws IOException {String key = UUID.randomUUID().toString();String code = producer.createText();BufferedImage image = producer.createImage(code);ByteArrayOutputStream outputStream = new ByteArrayOutputStream();ImageIO.write(image, "jpg", outputStream);BASE64Encoder encoder = new BASE64Encoder();String str = "data:image/jpeg;base64,";String base64Img = str + encoder.encode(outputStream.toByteArray());redisUtil.hset(Const.CAPTCHA_KEY, key, code, 120);return Result.succ(MapUtil.builder().put("userKey", key).put("captcherImg", base64Img).build());
}
③ 验证码的filter
  1. 过滤器将来放到验证用户名密码过滤器前端
  2. 需要先判断请求是否是登录请求,若是登录请求,则进行验证码校验。若不是,则直接跳过这个过滤器。
  3. CaptchaFilter继承了OncePerRequestFilter抽象类,该抽象类在每次请求时只执行一次过滤,即它的作用就是保证一次请求只通过一次filter,而不需要重复执行。
@Component
public class CaptchaFilter extends OncePerRequestFilter {@AutowiredRedisUtil redisUtil;@AutowiredLoginFailureHandler loginFailureHandler;@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {String url = httpServletRequest.getRequestURI();if ("/login".equals(url) && httpServletRequest.getMethod().equals("POST")) {// 校验验证码try {validate(httpServletRequest);} catch (CaptchaException e) {// 交给认证失败处理器loginFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);}}filterChain.doFilter(httpServletRequest, httpServletResponse);}// 校验验证码逻辑private void validate(HttpServletRequest httpServletRequest) {   String code = httpServletRequest.getParameter("code");String key = httpServletRequest.getParameter("userKey");if (StringUtils.isBlank(code) || StringUtils.isBlank(key)) {throw new CaptchaException("验证码错误");}if (!code.equals(redisUtil.hget(Const.CAPTCHA_KEY, key))) {throw new CaptchaException("验证码错误");}// 若验证码正确,执行以下语句// 一次性使用redisUtil.hdel(Const.CAPTCHA_KEY, key);}
}
⑤ 继承BasicAuthenticationFilter,实现用户验证
  1. login-form(登录表单认证):使用基于表单的用户界面进行认证。用户在登录页面中输入用户名和密码,提交表单后,Spring Security会验证用户凭据并完成认证过程。
  2. httpBasic(基本身份验证):在HTTP请求头中发送用户名和密码进行认证。客户端会在每个请求中添加Authorization头,其中包含Basic认证信息。
  1. UsernamePasswordAuthenticationFilter过滤器用于处理基于表单方式的登录验证。
  2. BasicAuthenticationFilter用于处理基于HTTP Basic方式的登录验证

JwtAuthenticationFilter继承了BasicAuthenticationFilter,该类用于普通http请求进行身份认证,该类有一个重要属性:AuthenticationManager,表示认证管理器,它是一个接口,它的默认实现类是ProviderManager


public class JwtAuthenticationFilter extends BasicAuthenticationFilter {  @AutowiredJwtUtils jwtUtils;@AutowiredUserDetailServiceImpl userDetailService;@AutowiredSysUserService sysUserService;public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {super(authenticationManager);}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {String jwt = request.getHeader(jwtUtils.getHeader());// 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的// 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口if (StrUtil.isBlankOrUndefined(jwt)) {     chain.doFilter(request, response);return;}Claims claim = jwtUtils.getClaimsByToken(jwt);if (claim == null) {throw new JwtException("token 异常");}if (jwtUtils.isTokenExpired(claim)) {throw new JwtException("token 已过期");}String username = claim.getSubject();// 获取用户的权限等信息SysUser sysUser = sysUserService.getByUsername(username);// 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, userDetailService.getUserAuthority(sysUser.getId()));SecurityContextHolder.getContext().setAuthentication(token);chain.doFilter(request, response);}
}
⑥ 认证失败的JwtAuthenticationEntryPoint(用户未登录处理类)

当BasicAuthenticationFilter认证失败的时候会进入AuthenticationEntryPoint,我们定义JWT认证失败处理器JwtAuthenticationEntryPoint,使其实现AuthenticationEntryPoint接口

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {httpServletResponse.setContentType("application/json;charset=UTF-8");httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);ServletOutputStream outputStream = httpServletResponse.getOutputStream();Result result = Result.fail("请先登录");outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}
⑦ 暂无权限处理类(AccessDeniedHandler,状态码403)
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {httpServletResponse.setContentType("application/json;charset=UTF-8");httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);ServletOutputStream outputStream = httpServletResponse.getOutputStream();Result result = Result.fail(e.getMessage());outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}
⑧ 登出处理器LogoutSuccessHandler
@Component
public class JWTLogoutSuccessHandler implements LogoutSuccessHandler {@AutowiredJwtUtils jwtUtils;@Overridepublic void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {if (authentication != null) {new SecurityContextLogoutHandler().logout(httpServletRequest, httpServletResponse, authentication);}httpServletResponse.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = httpServletResponse.getOutputStream();httpServletResponse.setHeader(jwtUtils.getHeader(), "");Result result = Result.succ("SuccessLogout");outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));outputStream.flush();outputStream.close();}
}
⑨ 自定义AccountUser类实现UserDetails(拓展原有的UserDetails)
public class AccountUser implements UserDetails {private Long userId;private static final long serialVersionUID = 540L;private static final Log logger = LogFactory.getLog(User.class);private String password;private final String username;private final Collection<? extends GrantedAuthority> authorities;private final boolean accountNonExpired;private final boolean accountNonLocked;private final boolean credentialsNonExpired;private final boolean enabled;public AccountUser(Long userId, String username, String password, Collection<? extends GrantedAuthority> authorities) {this(userId, username, password, true, true, true, true, authorities);}public AccountUser(Long userId, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {Assert.isTrue(username != null && !"".equals(username) && password != null, "Cannot pass null or empty values to constructor");this.userId = userId;this.username = username;this.password = password;this.enabled = enabled;this.accountNonExpired = accountNonExpired;this.credentialsNonExpired = credentialsNonExpired;this.accountNonLocked = accountNonLocked;this.authorities = authorities;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authorities;}@Overridepublic String getPassword() {return this.password;}@Overridepublic String getUsername() {return this.username;}@Overridepublic boolean isAccountNonExpired() {return this.accountNonExpired;}@Overridepublic boolean isAccountNonLocked() {return this.accountNonLocked;}@Overridepublic boolean isCredentialsNonExpired() {return this.credentialsNonExpired;}@Overridepublic boolean isEnabled() {return this.enabled;}
}
⑩ 实现UserDetailsService,用于和数据库比对
@Service
public class UserDetailServiceImpl implements UserDetailsService {@AutowiredSysUserService sysUserService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {SysUser sysUser = sysUserService.getByUsername(username);if (sysUser == null) {throw new UsernameNotFoundException("用户名或密码错误");}return new AccountUser(sysUser.getId(), sysUser.getUsername(), sysUser.getPassword(), getUserAuthority(sysUser.getId()));}
}
⑩① 密码的加密解密
@NoArgsConstructor
public class PasswordEncoder extends BCryptPasswordEncoder {@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {// 接收到的前端的密码String pwd = rawPassword.toString();// 进行rsa解密try {pwd = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, pwd);} catch (Exception e) {throw new BadCredentialsException(e.getMessage());}if (encodedPassword != null && encodedPassword.length() != 0) {return BCrypt.checkpw(pwd, encodedPassword);} else {return false;}}
}
⑩② SecurityConfig配置类
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {@AutowiredLoginFailureHandler loginFailureHandler;@AutowiredLoginSuccessHandler loginSuccessHandler;@AutowiredCaptchaFilter captchaFilter;@AutowiredJwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;@AutowiredJwtAccessDeniedHandler jwtAccessDeniedHandler;@AutowiredUserDetailServiceImpl userDetailService;@AutowiredJWTLogoutSuccessHandler jwtLogoutSuccessHandler;@BeanJwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager());return jwtAuthenticationFilter;}private static final String[] URL_WHITELIST = {"/login","/logout","/captcha","/favicon.ico"};@BeanPasswordEncoder PasswordEncoder() {return new PasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors().and().csrf().disable()// 登录配置.httpBasic().successHandler(loginSuccessHandler).failureHandler(loginFailureHandler).and().logout().logoutSuccessHandler(jwtLogoutSuccessHandler)// 禁用session.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)// 配置拦截规则.and().authorizeRequests().antMatchers(URL_WHITELIST).permitAll().anyRequest().authenticated()// 异常处理器.and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).accessDeniedHandler(jwtAccessDeniedHandler)// 配置自定义的过滤器.and().addFilter(jwtAuthenticationFilter())// 验证码过滤器放在UsernamePassword过滤器之前.addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class);}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailService);}
}

比较好的代码推荐
https://zhuanlan.zhihu.com/p/585835490

好文推荐

④ 回顾一下Security的登陆流程


在这里插入图片描述

这篇关于前端带你学后端系列 ⑥【安全框架Spring Security篇二】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/509364

相关文章

Java反转字符串的五种方法总结

《Java反转字符串的五种方法总结》:本文主要介绍五种在Java中反转字符串的方法,包括使用StringBuilder的reverse()方法、字符数组、自定义StringBuilder方法、直接... 目录前言方法一:使用StringBuilder的reverse()方法方法二:使用字符数组方法三:使用自

JAVA封装多线程实现的方式及原理

《JAVA封装多线程实现的方式及原理》:本文主要介绍Java中封装多线程的原理和常见方式,通过封装可以简化多线程的使用,提高安全性,并增强代码的可维护性和可扩展性,需要的朋友可以参考下... 目录前言一、封装的目标二、常见的封装方式及原理总结前言在 Java 中,封装多线程的原理主要围绕着将多线程相关的操

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

Spring Cloud之注册中心Nacos的使用详解

《SpringCloud之注册中心Nacos的使用详解》本文介绍SpringCloudAlibaba中的Nacos组件,对比了Nacos与Eureka的区别,展示了如何在项目中引入SpringClo... 目录Naacos服务注册/服务发现引⼊Spring Cloud Alibaba依赖引入Naco编程s依

java导出pdf文件的详细实现方法

《java导出pdf文件的详细实现方法》:本文主要介绍java导出pdf文件的详细实现方法,包括制作模板、获取中文字体文件、实现后端服务以及前端发起请求并生成下载链接,需要的朋友可以参考下... 目录使用注意点包含内容1、制作pdf模板2、获取pdf导出中文需要的文件3、实现4、前端发起请求并生成下载链接使

Java springBoot初步使用websocket的代码示例

《JavaspringBoot初步使用websocket的代码示例》:本文主要介绍JavaspringBoot初步使用websocket的相关资料,WebSocket是一种实现实时双向通信的协... 目录一、什么是websocket二、依赖坐标地址1.springBoot父级依赖2.springBoot依赖

如何用java对接微信小程序下单后的发货接口

《如何用java对接微信小程序下单后的发货接口》:本文主要介绍在微信小程序后台实现发货通知的步骤,包括获取Access_token、使用RestTemplate调用发货接口、处理AccessTok... 目录配置参数 调用代码获取Access_token调用发货的接口类注意点总结配置参数 首先需要获取Ac

Java逻辑运算符之&&、|| 与&、 |的区别及应用

《Java逻辑运算符之&&、||与&、|的区别及应用》:本文主要介绍Java逻辑运算符之&&、||与&、|的区别及应用的相关资料,分别是&&、||与&、|,并探讨了它们在不同应用场景中... 目录前言一、基本概念与运算符介绍二、短路与与非短路与:&& 与 & 的区别1. &&:短路与(AND)2. &:非短

Java的volatile和sychronized底层实现原理解析

《Java的volatile和sychronized底层实现原理解析》文章详细介绍了Java中的synchronized和volatile关键字的底层实现原理,包括字节码层面、JVM层面的实现细节,以... 目录1. 概览2. Synchronized2.1 字节码层面2.2 JVM层面2.2.1 ente

什么是 Java 的 CyclicBarrier(代码示例)

《什么是Java的CyclicBarrier(代码示例)》CyclicBarrier是多线程协同的利器,适合需要多次同步的场景,本文通过代码示例讲解什么是Java的CyclicBarrier,感... 你的回答(口语化,面试场景)面试官:什么是 Java 的 CyclicBarrier?你:好的,我来举个例