看完这一篇,你就对 Spring Security 略窥门径了 | 原力计划

作者 | BoCong-Deng

来源 | CSDN 博客,责编 | 夕颜

头图 | CSDN 下载自东方 IC

出品 | CSDN(ID:CSDNnews)

写在前面

开发Web应用,对页面的安全控制通常是必须的。比如:对于没有访问权限的用户需要转到登录表单页面。要实现访问控制的方法多种多样,可以通过Aop、拦截器实现,也可以通过框架实现,例如:Apache Shiro、Spring Security。我们这里要讲的Spring Security 就是一个Spring生态中关于安全方面的框架。它能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案。

默认认证用户名密码

项目pom.xml添加spring-boot-starter-security依赖

1<dependency>
2    <groupId>org.springframework.boot</groupId>
3    <artifactId>spring-boot-starter-security</artifactId>
4</dependency>

重启你的应用。再次打开页面,你讲看到一个登录页面

既然跳到了登录页面,那么这个时候我们就会想,这个登录的用户名以及密码是什么呢?让我们来从SpringBoot源码寻找一下。你搜一下输出日志,会看到下面一段输出:

这段日志是UserDetailsServiceAutoConfiguration类里面的如下方法输出的:

通过上面的这个类,我们可以看出,是SecurityProperties这个Bean管理了用户名和密码。在SecurityProperties里面的一个内部静态类User类里面,管理了默认的认证的用户名与密码。代码如下

 1@ConfigurationProperties(2    prefix = "spring.security"3)4public class SecurityProperties {5    public static final int BASIC_AUTH_ORDER = 2147483642;6    public static final int IGNORED_ORDER = -2147483648;7    public static final int DEFAULT_FILTER_ORDER = -100;8    private final SecurityProperties.Filter filter = new SecurityProperties.Filter();9    private SecurityProperties.User user = new SecurityProperties.User();
10
11    public SecurityProperties() {
12    }
13
14    public SecurityProperties.User getUser() {
15        return this.user;
16    }
17
18    public SecurityProperties.Filter getFilter() {
19        return this.filter;
20    }
21
22    public static class User {
23        private String name = "user";
24        private String password = UUID.randomUUID().toString();
25        private List<String> roles = new ArrayList();
26        private boolean passwordGenerated = true;
27
28        public User() {
29        }
30
31        public String getName() {
32            return this.name;
33        }
34
35        public void setName(String name) {
36            this.name = name;
37        }
38
39        public String getPassword() {
40            return this.password;
41        }
42
43        public void setPassword(String password) {
44            if (StringUtils.hasLength(password)) {
45                this.passwordGenerated = false;
46                this.password = password;
47            }
48        }
49
50        public List<String> getRoles() {
51            return this.roles;
52        }
53
54        public void setRoles(List<String> roles) {
55            this.roles = new ArrayList(roles);
56        }
57
58        public boolean isPasswordGenerated() {
59            return this.passwordGenerated;
60        }
61    }
62
63    public static class Filter {
64        private int order = -100;
65        private Set<DispatcherType> dispatcherTypes;
66
67        public Filter() {
68            this.dispatcherTypes = new HashSet(Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
69        }
70
71        public int getOrder() {
72            return this.order;
73        }
74
75        public void setOrder(int order) {
76            this.order = order;
77        }
78
79        public Set<DispatcherType> getDispatcherTypes() {
80            return this.dispatcherTypes;
81        }
82
83        public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) {
84            this.dispatcherTypes = dispatcherTypes;
85        }
86    }
87}

综上所述,security默认的用户名是user, 默认密码是应用启动的时候,通过UUID算法随机生成的,默认的role是"USER"。当然,如果我们想简单改一下这个用户名密码,可以在application.properties配置你的用户名密码,例如


当然这只是一个初级的配置,更复杂的配置,可以分不用角色,在控制范围上,能够拦截到方法级别的权限控制。

内存用户名密码认证

在上面的内容,我们什么都没做,就添加了spring-boot-starter-security依赖,整个应用就有了默认的认证安全机制。下面,我们来定制用户名密码。写一个继承了 WebSecurityConfigurerAdapter的配置类,具体内容如下

 1import org.springframework.context.annotation.Configuration;2import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;3import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;4import org.springframework.security.config.annotation.web.builders.HttpSecurity;5import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;6import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;78@Configuration9@EnableWebSecurity
10@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
11public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
12    @Override
13    protected void configure(HttpSecurity http) throws Exception {
14        super.configure(http);
15    }
16
17    @Override
18    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
19        auth.inMemoryAuthentication()
20                .passwordEncoder(new BCryptPasswordEncoder())
21                .withUser("admin")
22                .password(new BCryptPasswordEncoder().encode("1234567"))
23                .roles("USER");
24    }
25}

这里对上面的代码进行简要说明:

  • Spring security 5.0中新增了多种加密方式,也改变了默认的密码格式。需要修改一下configure中的代码,我们要将前端传过来的密码进行某种方式加密,Spring Security 官方推荐的是使用bcrypt加密方式。inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()),这相当于登陆时用BCrypt加密方式对用户密码进行处理。以前的".password("123")" 变成了 “.password(new BCryptPasswordEncoder().encode("123"))”,这相当于对内存中的密码进行Bcrypt编码加密。如果比对时一致,说明密码正确,才允许登陆。

  • 通过 @EnableWebSecurity注解开启Spring Security的功能。使用@EnableGlobalMethodSecurity(prePostEnabled = true)这个注解,可以开启security的注解,我们可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解。

  • 继承 WebSecurityConfigurerAdapter 类,并重写它的方法来设置一些web安全的细节。我们结合@EnableWebSecurity注解和继承WebSecurityConfigurerAdapter,来给我们的系统加上基于web的安全机制。

  • 在configure(HttpSecurity http)方法里面,我们进入到源码中,就会看到默认的认证代码是:

从方法名我们基本可以看懂这些方法的功能。上面的那个默认的登录页面,就是SpringBoot默认的用户名密码认证的login页面。我们使用SpringBoot默认的配置super.configure(http),它通过 authorizeRequests() 定义哪些URL需要被保护、哪些不需要被保护。默认配置是所有访问页面都需要认证,才可以访问。

  • 通过 formLogin() 定义当需要用户登录时候,转到的登录页面。

  • configureGlobal(AuthenticationManagerBuilder auth) 方法,在内存中创建了一个用户,该用户的名称为root,密码为root,用户角色为USER。这个默认的登录页面是怎么冒出来的呢?是的,SpringBoot内置的,SpringBoot甚至给我们做好了一个极简的登录页面。这个登录页面是通过Filter实现的。具体的实现类是org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter。同时,这个DefaultLoginPageGeneratingFilter也是SpringBoot的默认内置的Filter。

输入用户名,密码,点击Login。不过,我们发现,SpringBoot应用的启动日志还是打印了如下一段:

但实际上,已经使用了我们定制的用户名密码了。如果我们要配置多个用户,多个角色,可参考使用如下示例的代码:

 1@Override2    protected void configure(AuthenticationManagerBuilder auth) throws Exception {3        auth.inMemoryAuthentication()4                .passwordEncoder(new BCryptPasswordEncoder())5                .withUser("admin")6                .password(new BCryptPasswordEncoder().encode("1234567"))7                .roles("USER")8                .and()9                .withUser("admin1")
10                .password(new BCryptPasswordEncoder().encode("123"))
11                .roles("ADMIN", "USER");
12    }

角色权限控制

当我们的系统功能模块当需求发展到一定程度时,会不同的用户,不同角色使用我们的系统。这样就要求我们的系统可以做到,能够对不同的系统功能模块,开放给对应的拥有其访问权限的用户使用。Spring Security提供了Spring EL表达式,允许我们在定义URL路径访问(@RequestMapping)的方法上面添加注解,来控制访问权限。在标注访问权限时,根据对应的表达式返回结果,控制访问权限:

1true,表示有权限
2fasle,表示无权限

Spring Security可用表达式对象的基类是SecurityExpressionRoot。

  1public abstract class SecurityExpressionRoot implements SecurityExpressionOperations {2    protected final Authentication authentication;3    private AuthenticationTrustResolver trustResolver;4    private RoleHierarchy roleHierarchy;5    private Set<String> roles;6    private String defaultRolePrefix = "ROLE_";7    public final boolean permitAll = true;8    public final boolean denyAll = false;9    private PermissionEvaluator permissionEvaluator;10    public final String read = "read";11    public final String write = "write";12    public final String create = "create";13    public final String delete = "delete";14    public final String admin = "administration";1516    public SecurityExpressionRoot(Authentication authentication) {17        if (authentication == null) {18            throw new IllegalArgumentException("Authentication object cannot be null");19        } else {20            this.authentication = authentication;21        }22    }2324    public final boolean hasAuthority(String authority) {25        return this.hasAnyAuthority(authority);26    }2728    public final boolean hasAnyAuthority(String... authorities) {29        return this.hasAnyAuthorityName((String)null, authorities);30    }3132    public final boolean hasRole(String role) {33        return this.hasAnyRole(role);34    }3536    public final boolean hasAnyRole(String... roles) {37        return this.hasAnyAuthorityName(this.defaultRolePrefix, roles);38    }3940    private boolean hasAnyAuthorityName(String prefix, String... roles) {41        Set<String> roleSet = this.getAuthoritySet();42        String[] var4 = roles;43        int var5 = roles.length;4445        for(int var6 = 0; var6 < var5; ++var6) {46            String role = var4[var6];47            String defaultedRole = getRoleWithDefaultPrefix(prefix, role);48            if (roleSet.contains(defaultedRole)) {49                return true;50            }51        }5253        return false;54    }5556    public final Authentication getAuthentication() {57        return this.authentication;58    }5960    public final boolean permitAll() {61        return true;62    }6364    public final boolean denyAll() {65        return false;66    }6768    public final boolean isAnonymous() {69        return this.trustResolver.isAnonymous(this.authentication);70    }7172    public final boolean isAuthenticated() {73        return !this.isAnonymous();74    }7576    public final boolean isRememberMe() {77        return this.trustResolver.isRememberMe(this.authentication);78    }7980    public final boolean isFullyAuthenticated() {81        return !this.trustResolver.isAnonymous(this.authentication) && !this.trustResolver.isRememberMe(this.authentication);82    }8384    public Object getPrincipal() {85        return this.authentication.getPrincipal();86    }8788    public void setTrustResolver(AuthenticationTrustResolver trustResolver) {89        this.trustResolver = trustResolver;90    }9192    public void setRoleHierarchy(RoleHierarchy roleHierarchy) {93        this.roleHierarchy = roleHierarchy;94    }9596    public void setDefaultRolePrefix(String defaultRolePrefix) {97        this.defaultRolePrefix = defaultRolePrefix;98    }99
100    private Set<String> getAuthoritySet() {
101        if (this.roles == null) {
102            Collection<? extends GrantedAuthority> userAuthorities = this.authentication.getAuthorities();
103            if (this.roleHierarchy != null) {
104                userAuthorities = this.roleHierarchy.getReachableGrantedAuthorities(userAuthorities);
105            }
106
107            this.roles = AuthorityUtils.authorityListToSet(userAuthorities);
108        }
109
110        return this.roles;
111    }
112
113    public boolean hasPermission(Object target, Object permission) {
114        return this.permissionEvaluator.hasPermission(this.authentication, target, permission);
115    }
116
117    public boolean hasPermission(Object targetId, String targetType, Object permission) {
118        return this.permissionEvaluator.hasPermission(this.authentication, (Serializable)targetId, targetType, permission);
119    }
120
121    public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
122        this.permissionEvaluator = permissionEvaluator;
123    }
124
125    private static String getRoleWithDefaultPrefix(String defaultRolePrefix, String role) {
126        if (role == null) {
127            return role;
128        } else if (defaultRolePrefix != null && defaultRolePrefix.length() != 0) {
129            return role.startsWith(defaultRolePrefix) ? role : defaultRolePrefix + role;
130        } else {
131            return role;
132        }
133    }
134}

通过阅读源码,我们可以更加深刻的理解其EL写法,并在写代码的时候正确的使用。变量defaultRolePrefix硬编码约定了role的前缀是"ROLE_"。同时,我们可以看出hasRole跟hasAnyRole是一样的。hasAnyRole是调用的hasAnyAuthorityName(defaultRolePrefix, roles)。所以,我们在学习一个框架或者一门技术的时候,最准确的就是源码。通过源码,我们可以更好更深入的理解技术的本质。

SecurityExpressionRoot为我们提供的使用Spring EL表达式总结如下:

在Controller方法上添加@PreAuthorize这个注解,value="hasRole('ADMIN')")是Spring-EL expression,当表达式值为true,标识这个方法可以被调用。如果表达式值是false,标识此方法无权限访问。

在Spring Security里获取当前登录认证通过的用户信息

如果我们想要在前端页面显示当前登录的用户怎么办呢?在在Spring Security里面怎样获取当前登录认证通过的用户信息?下面我们就来探讨这个问题。其实很好办。我们添加一个LoginFilter,默认拦截所有请求,把当前登录的用户放到系统session中即可。在Spring Security中,用户信息保存在SecurityContextHolder中。Spring Security使用一个Authentication对象来持有所有系统的安全认证相关的信息。这个信息的内容格式如下:

 1{2    "accountNonExpired":true,3    "accountNonLocked":true,4    "authorities":[{5        "authority":"ROLE_ADMIN"6    },{7        "authority":"ROLE_USER"8    }],9    "credentialsNonExpired":true,
10    "enabled":true,
11    "username":"root"
12}

这个Authentication对象信息其实就是User实体的信息,类似如下(当然,密码没放进来)。

 1public class User implements UserDetails, CredentialsContainer {2    private String password;3    private final String username;4    private final Set<GrantedAuthority> authorities;5    private final boolean accountNonExpired;6    private final boolean accountNonLocked;7    private final boolean credentialsNonExpired;8    private final boolean enabled;9        ....
10}

我们可以使用下面的代码(Java)获得当前身份验证的用户的名称:

1Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
2
3if (principal instanceof UserDetails) {
4    String username = ((UserDetails)principal).getUsername();
5} else {
6    String username = principal.toString();
7}

通过调用getContext()返回的对象是SecurityContext的实例对象,该实例对象保存在ThreadLocal线程本地存储中。使用Spring Security框架,通常的认证机制都是返回UserDetails实例,通过如上这种方式,我们就可以拿到认证登录的用户信息。

用数据库存储用户和角色,实现安全认证

很多时候,我们需要的是实现一个用数据库存储用户和角色,实现系统的安全认证。为了简化讲解,本例中在权限角色上,我们简单设计两个用户角色:USER,ADMIN。我们设计页面的权限如下:

  • 首页/ : 所有人可访问

  • 登录页 /login: 所有人可访问

  • 普通用户权限页 /httpapi, /httpsuite: 登录后的用户都可访问

  • 管理员权限页 /httpreport :仅管理员可访问

  • 无权限提醒页:当一个用户访问了其没有权限的页面,我们使用全局统一的异常处理页面提示。

配置Spring Security

我们首先使用Spring Security帮我们做登录、登出的处理,以及当用户未登录时只能访问: http://localhost:8080/ 以及 http://localhost:8080/login 两个页面。同样的,我们要写一个继承WebSecurityConfigurerAdapter的配置类:

 1import com.springboot.in.action.service.LightSwordUserDetailService;2import org.springframework.context.annotation.Bean;3import org.springframework.context.annotation.Configuration;4import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;5import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;6import org.springframework.security.config.annotation.web.builders.HttpSecurity;7import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;8import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;9import org.springframework.security.core.userdetails.UserDetailsService;
10
11/**
12 * Created by jack on 2017/4/27.
13 */
14
15@Configuration
16@EnableWebSecurity
17@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
18//使用@EnableGlobalMethodSecurity(prePostEnabled = true)
19// 这个注解,可以开启security的注解,我们可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解。
20public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
21    @Override
22    @Bean
23    public UserDetailsService userDetailsService() { //覆盖写userDetailsService方法 (1)
24        return new AdminUserDetailService();
25
26    }
27
28    /**
29     * If subclassed this will potentially override subclass configure(HttpSecurity)
30     *
31     * @param http
32     * @throws Exception
33     */
34    @Override
35    protected void configure(HttpSecurity http) throws Exception {
36        //super.configure(http);
37        http.csrf().disable();
38
39        http.authorizeRequests()
40            .antMatchers("/").permitAll()
41            .antMatchers("/amchart/**",
42                "/bootstrap/**",
43                "/build/**",
44                "/css/**",
45                "/dist/**",
46                "/documentation/**",
47                "/fonts/**",
48                "/js/**",
49                "/pages/**",
50                "/plugins/**"
51            ).permitAll() //默认不拦截静态资源的url pattern (2)
52            .anyRequest().authenticated().and()
53            .formLogin().loginPage("/login")// 登录url请求路径 (3)
54            .defaultSuccessUrl("/httpapi").permitAll().and() // 登录成功跳转路径url(4)
55            .logout().permitAll();
56
57        http.logout().logoutSuccessUrl("/"); // 退出默认跳转页面 (5)
58
59    }
60
61    @Override
62    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
63        //AuthenticationManager使用我们的 Service来获取用户信息,Service可以自己写,其实就是简单的读取数据库的操作
64        auth.userDetailsService(()); // (6)
65    }
66
67}

上面的代码只做了基本的配置,其中:

  • 覆盖写userDetailsService方法,具体的AdminUserDetailsService实现类,就是之前说的获取用户信息的service层类。

  • 默认不拦截静态资源的url pattern。我们也可以用下面的WebSecurity这个方式跳过静态资源的认证。

1public void configure(WebSecurity web) throws Exception {
2    web
3        .ignoring()
4        .antMatchers("/resourcesDir/**");
5}
  • 跳转登录页面url请求路径为/login,我们需要定义一个Controller把路径映射到login.html。

  • 登录成功后跳转的路径为/httpapi

  • 退出后跳转到的url为/

  • 认证鉴权信息的Bean,采用我们自定义的从数据库中获取用户信息的AdminUserDetailService类。

我们同样使用@EnableGlobalMethodSecurity(prePostEnabled = true)这个注解,开启security的注解,这样我们可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解。

用户退出

我们在configure(HttpSecurity http)方法里面定义了任何权限都允许退出,当然SpringBoot集成Security的默认退出请求是/logout

1http.logout().logoutSuccessUrl("/"); // 退出默认跳转页面 (4)

配置错误处理页面

访问发生错误时,跳转到系统统一异常处理页面。我们首先添加一个GlobalExceptionHandlerAdvice,使用@ControllerAdvice注解:

 1import org.springframework.web.bind.annotation.{ControllerAdvice, ExceptionHandler}2import org.springframework.web.context.request.WebRequest3import org.springframework.web.servlet.ModelAndView45/**6  * Created by jack on 2017/4/27.7  */8@ControllerAdvice9class GlobalExceptionHandlerAdvice {
10  @ExceptionHandler(value = Exception.class)//表示捕捉到所有的异常,你也可以捕捉一个你自定义的异常
11    public ModelAndView exception(Exception exception, WebRequest request){
12        ModelAndView modelAndView = new ModelAndView("/error");
13        modelAndView.addObject("errorMessage", exception.getMessage());
14        modelAndView.addObject("stackTrace", exception.getStackTrace());
15        return modelAndView;
16    }
17}

其中,@ExceptionHandler(value = Exception.class),表示捕捉到所有的异常,这里你也可以捕捉一个你自定义的异常。比如说,针对安全认证的Exception,我们可以单独定义处理。此处不再赘述。

原文链接:

https://blog.csdn.net/DBC_121/article/details/104740273

为了让大家更好地了解开发者,CSDN特发起“开发者与AI大调查”活动。 点击阅读原文,填写调查问卷,您将获得价值299元的「2020 AI 开发者万人大会」在线直播门票一张哟~

推荐阅读:为何你的 SaaS 想法总是失败?没想清楚这 4 个原因可能会继续失败!
如何给女朋友解释什么是撞库、脱库和洗库?
开源的未来 10 年:中国开源社区建立是关键
万字好文:智能合约编写之Solidity的编程攻略,建议收藏!
Python 爬取疫情期间全球股市走向,笑不出来......
无代码时代来临,程序员如何保住饭碗?
真香,朕在看了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/518633.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java-冒泡排序

public class ArrayDemo07 {public static void main(String[] args) {int[] a {3,5,1,7,8,4};int[] sort sort(a);System.out.println(Arrays.toString(sort)); // [1, 3, 4, 5, 7, 8]}/*每次将 最大 或 最小的数 后移*/public static int[] sort(int[] array){// 临时变量…

借助 Cloud Toolkit 快速创建 Dubbo 工程

Cloud Toolkit 是一个 IDE 插件&#xff0c;帮助开发者更高效地开发、测试、诊断并部署应用。在最新版的插件中&#xff0c;提供了快速创建 Dubbo 工程的功能&#xff0c;下面就来快速体验下吧。 Dubbo 采用全 Spring 配置方式&#xff0c;透明化接入应用&#xff0c;对应用没…

vue-cli-service不是内部或外部命令,也不是可运行的程序

报错信息: “不是内部或外部命令&#xff0c;也不是可运行的程序” 步骤一: 检查package.json 中是否有 vue-cli-server,没有则需安装 步骤二 : 执行npm install命令 npm run serve

另一种声音:容器是不是未来?

作者 | Ian Eyberg译者 | 天道酬勤 责编 | 徐威龙封图| CSDN 下载于视觉中国CSDN 云计算旨在为读者提供更多角度的声音&#xff0c;本文仅代表作者个人观点&#xff0c;不代表CSDN云计算任何立场。前几天作者看到了这则推文&#xff0c;可以这么说&#xff0c;是它促使我开始就…

Java-稀疏数组

public class ArrayDemo08 {public static void main(String[] args) {// 1. 创建一个二维数组 11*11 0: 没有棋子 1: 黑棋 2: 白棋int[][] array1 new int[11][11];array1[1][2] 1;array1[2][3] 2;// 输出原始的数组System.out.println("输出原始的数组");for…

揭秘|每秒千万级的实时数据处理是怎么实现的?

1、设计背景 闲鱼目前实际生产部署环境越来越复杂&#xff0c;横向依赖各种服务盘宗错节&#xff0c;纵向依赖的运行环境也越来越复杂。当服务出现问题的时候&#xff0c;能否及时在海量的数据中定位到问题根因&#xff0c;成为考验闲鱼服务能力的一个严峻挑战。 线上出现问题…

Vue3.x 使用ref和reactive、toRef

文章目录一、使用ref1. 引入2. 定义ref变量3. 赋值.value4. return 返回值5. 将数据渲染到页面二、使用reactive 和 toRef1. 引入2. 定义reactive变量3. 赋值变量.对象4. return 返回值5. 将数据渲染到页面三、效果图源码3.1. 效果图3.2. 源码一、使用ref 1. 引入 从vue里面引…

同学,要不要来挑战双11零点流量洪峰?

阿里妹导读&#xff1a;双十一的零点&#xff0c;整个电商系统的请求速率到达峰值。如果将这些请求流量只分配给少部分 server&#xff0c;这些机器接收到的请求速率会远超过处理速率&#xff0c;新来的任务来不及处理&#xff0c;就会产生请求任务堆积。 今年的中间件性能挑战…

GitHub 疑遭中间人攻击,最大暗网托管商再被黑!

整理 | 伍杏玲出品 | 程序人生&#xff08;ID&#xff1a;coder_life&#xff09;近期&#xff0c;在全球关注新冠肺炎疫情之际&#xff0c;黑客却频频动作&#xff0c;发动攻击&#xff1a;GitHub 疑遭中间人攻击&#xff0c;无法访问从26日下午开始&#xff0c;有网友表示国内…

Java-什么是面向对象

https://www.bilibili.com/video/BV12J41137hu?p60

Jenkins Pipeline脚本优化:为Kubernetes应用部署增加状态检测

引言 在软件部署的世界中&#xff0c;Jenkins已经成为自动化流程的代名词。不断变化的技术环境要求我们持续改进部署流程以满足现代应用部署的需要。在本篇博客中&#xff0c;作为一位资深运维工程师&#xff0c;我将分享如何将Jenkins Pipeline进化至不仅能支持部署应用直至R…

Apache Flink 结合 Kafka 构建端到端的 Exactly-Once 处理

文章目录&#xff1a; Apache Flink 应用程序中的 Exactly-Once 语义Flink 应用程序端到端的 Exactly-Once 语义示例 Flink 应用程序启动预提交阶段在 Flink 中实现两阶段提交 Operator总结 Apache Flink 自2017年12月发布的1.4.0版本开始&#xff0c;为流计算引入了一个重要的…

一文教你如何使用 MongoDB 和 HATEOAS 创建 REST Web 服务

作者 | Ion Pascari译者 | 天道酬勤 责编 | 徐威龙封图| CSDN 下载于视觉中国最近&#xff0c;作者在把HATEOAS实现到REST Web服务时遇到了一件有趣的事情&#xff0c;而且他也很幸运地尝试了一个名为MongoDB的NoSQL数据库&#xff0c;他发现该数据库在许多不需要管理实务的不同…

使用NGINX作为HTTPS正向代理服务器

NGINX主要设计作为反向代理服务器&#xff0c;但随着NGINX的发展&#xff0c;它同样能作为正向代理的选项之一。正向代理本身并不复杂&#xff0c;而如何代理加密的HTTPS流量是正向代理需要解决的主要问题。本文将介绍利用NGINX来正向代理HTTPS流量两种方案&#xff0c;及其使用…

IoT SaaS加速器——助力阿尔茨海默病人护理

场景介绍 阿尔茨海默病&#xff0c;是导致中老年人认知功能障碍的最常见疾病之一&#xff0c;是发生在老年期及老年前期的一种原发性退行性脑病。据估计&#xff0c;全世界痴呆症患者数量为4700万&#xff0c;到2030年将达到7500万人。痴呆症患者数量到2050年预计将是现在的近…

一个数据科学家需要哪些核心工具包?

作者 | Rebecca Vickery译者 | 天道酬勤 责编 | 徐威龙封图| CSDN 下载于视觉中国数据科学家的主要作用是将机器学习、统计方法和探索性分析应用到数据中&#xff0c;来提取见解并帮助制定决策。 编程和计算工具的使用对该角色来说必不可少。 实际上&#xff0c;许多人都用这句…

Java-静态方法、非静态方法

// 学生类 public class Student {// 静态方法 staticpublic static void say01(){System.out.println("学生01 静态方法说话了");}// 非静态方法public void say02(){System.out.println("学生02 非静态方法说话了");} }静态方法、非静态方法 public cla…

SpringBoot2.x Flowable 6.4.2 开源项目

文章目录一、项目服务端初始化1. 创建数据库2. 初始化表结构.3. 表结构补充4. 配置文件修改5. 下载依赖6. 异常解决7. 启动服务端二、前端初始化2.1. 安装Node(V12.x.x以上)和NPM(V6.x.x以上)2.2. 安装淘宝镜像2.2. 初始化前端项目2.3. 启动项目2.4. web登录页面2.5. 效果图三、…

MongoDB 4.2 新特性解读

云数据库 MongoDB 版 基于飞天分布式系统和高性能存储&#xff0c;提供三节点副本集的高可用架构&#xff0c;容灾切换&#xff0c;故障迁移完全透明化。并提供专业的数据库在线扩容、备份回滚、性能优化等解决方案。 了解更多 MongoDB World 2019 上发布新版本 MongoDB 4.2…

Java-类与对象的创建

// 学生类 public class Student {// 属性String name; // 默认 nullint age; // 默认 0// 方法public void study(){System.out.println(this.name " 在学习");} }public class Application {public static void main(String[] args) {// 实例化后会返回一个自己…