spring security 自定义图形验证码(web/前后端分离)

一、准备工作

1.1 导入pom 所需依赖

 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.3</version><!-- <version>2.7.18</version>--></parent><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!-- thymeleaf 相关依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

1.2 常量类

/***  图形验证码*/public static final String SESSION_IMAGE = "session-verifyimage";/***  登录的url*/public static final String LOGIN_URL = "/user/login";

二、web端自定义图像验证码

2.1  配置security 配置文件

package com.fasion.config;import com.fasion.security.LoginImageVerifyFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** @Author: LQ* @Date 2024/8/26 20:49* @Description: security 配置*/
@Configuration
@Slf4j
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {/*** 自定义数据源,从内存中,后期自己写一个mybatis 从数据库查询* @throws Exception*/@Beanpublic UserDetailsService userDetailsService() {InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();userDetailsManager.createUser(User.withUsername("test").password("{noop}12345").authorities("admin").build());return userDetailsManager;}/***  自定义authenticationManager 管理器,将自定义的数据源加到其中* @throws Exception*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService());}/***  用自己的认证管理器* @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/***  登录   自定义过滤器* @return*/@Beanpublic LoginImageVerifyFilter loginImageVerifyFilter() throws Exception {LoginImageVerifyFilter verifyFilter = new LoginImageVerifyFilter();verifyFilter.setFilterProcessesUrl("/login.do");// 认证地址verifyFilter.setUsernameParameter("loginId");verifyFilter.setPasswordParameter("loginPwd");verifyFilter.setVerifyImageParams("imageCode");// 图像验证码的参数// 认证成功处理逻辑verifyFilter.setAuthenticationSuccessHandler((req,resp,auth) -> {resp.sendRedirect("/main.html");});// 认证失败处理逻辑verifyFilter.setAuthenticationFailureHandler((req,resp,ex) -> {log.info("ex信息:{}",ex.getMessage());req.getSession().setAttribute("errMsg",ex.getMessage());resp.sendRedirect("/");// 跳到首页});// 自定义自己的管理器verifyFilter.setAuthenticationManager(authenticationManagerBean());return verifyFilter;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().mvcMatchers("/").permitAll()   //放行登录首页.mvcMatchers("/kap.jpg").permitAll() // 放行图像验证码//.mvcMatchers("/static/**").permitAll() // 静态目录放行.anyRequest().authenticated().and().formLogin() //表单设置.and().csrf().disable();// 关闭csrf 防护// 自定义过滤器替换默认的http.addFilterAt(loginImageVerifyFilter(), UsernamePasswordAuthenticationFilter.class);}
}

2.2  web端配置

package com.fasion.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @Author: LQ* @Date 2024/8/26 20:55* @Description:传统web开发*/
@Configuration
public class WebConfiguration implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");registry.addViewController("/main.html").setViewName("main");}//    @Override
//    public void addResourceHandlers(ResourceHandlerRegistry registry) {
//        registry.addResourceHandler("/static/").addResourceLocations("/static/**");
//    }
}

2.3 图片验证码工具生成类

该类是利用hutool 包提供的工具类生成图片验证码,具体请参考文档   概述 | Hutool

,由浏览器直接写出图片,该地方如果是集群环境可以将图形验证码的code存到redis中,登录时候再取出来验证;

package com.fasion.controller;import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import com.fasion.constants.ComConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.io.IOException;/***  图像验证码*/
@Controller
@Slf4j
public class ComController {/***  获取图像验证码* @param response*/@RequestMapping("kap.jpg")public void getVerifyImage(HttpSession session,HttpServletResponse response) {RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);//定义图形验证码的长、宽、验证码位数、干扰线数量LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(120, 40,4,19);lineCaptcha.setGenerator(randomGenerator);lineCaptcha.createCode();//设置背景颜色lineCaptcha.setBackground(new Color(249, 251, 220));//生成四位验证码String code = lineCaptcha.getCode();log.info("图形验证码生成成功:{}",code);session.setAttribute(ComConstants.SESSION_IMAGE,code);response.setContentType("image/jpeg");response.setHeader("Pragma", "no-cache");response.setHeader("Cache-Control", "no-cache");try {lineCaptcha.write(response.getOutputStream());} catch (IOException e) {log.error("图像验证码获取失败:",e);}}}

2.4 验证码过滤器

package com.fasion.security;import com.fasion.constants.ComConstants;
import com.fasion.exception.CustomerException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.ObjectUtils;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** @Author: LQ* @Date 2024/8/26 20:58* @Description: 登录验证,图形验证码*/
@Slf4j
public class LoginImageVerifyFilter extends UsernamePasswordAuthenticationFilter {private String verifyImageParams = "captcha";@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (!request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}// 获取图像验证码String imageCode = request.getParameter(getVerifyImageParams());String realCode = (String) request.getSession().getAttribute(ComConstants.SESSION_IMAGE);log.info("传过来的图像验证码为:{},session中实际的是:{}",imageCode,realCode);if (!ObjectUtils.isEmpty(imageCode) && !ObjectUtils.isEmpty(realCode) &&imageCode.equalsIgnoreCase(realCode)) {// 调用父类的认证方法return super.attemptAuthentication(request,response);}throw new CustomerException("图像验证码不正确!!!");}public String getVerifyImageParams() {return verifyImageParams;}public void setVerifyImageParams(String verifyImageParams) {this.verifyImageParams = verifyImageParams;}
}

2.5 自定义异常类

package com.fasion.exception;import org.springframework.security.core.AuthenticationException;/*** @Author: LQ* @Date 2024/8/26 21:07* @Description: 自定义异常*/
public class CustomerException extends AuthenticationException {public CustomerException(String msg) {super(msg);}
}

2.6 前端页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>登录页</title><!-- 引入样式 --><style type="text/css">#app{width: 600px;margin: 28px auto 10px }img{cursor: pointer;}</style>
</head>
<body><div id="app"><form th:action="@{/login.do}" method="post" ><div><label>用户名:</label><input type="text" name="loginId"></div><div><label>密码:</label><input type="text" name="loginPwd" ></div><div><label>图像验证码:</label><input type="text" name="imageCode"><img src="/kap.jpg"></div><div><label>错误信息:<span th:text="${session.errMsg}"></span></label></div><div><button type="submit" name="登录">登录</button></div></form></div></body>
</html>

2.6.1 前端效果

2.6.2 登录失败展示效果

2.6.3 登录成功


三、前后端分离自定义验证码(json数据格式)

3.1  security 配置

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {/***  自定义数据源* @return*/@Beanpublic UserDetailsService userDetailsService() {InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();userDetailsManager.createUser(User.withUsername("test").password("{noop}1234").authorities("admin").build());return userDetailsManager;}/***  配置数据源**/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService());}/***  显示指定自己的 AuthenticationManager* @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Beanpublic LoginVerifyCaptchaFilter loginVerifyImgFilter() throws Exception {LoginVerifyCaptchaFilter filter = new LoginVerifyCaptchaFilter();filter.setImageParams("verifyImg");// 图形验证码请求参数filter.setUsernameParameter("loginId");filter.setPasswordParameter("pwd");filter.setFilterProcessesUrl("/login.do");// 成功的响应filter.setAuthenticationSuccessHandler((req,resp,auth) -> {Map<String,Object> resMap = new HashMap<>();resMap.put("code","0000");resMap.put("msg","登录成功!");resMap.put("data",auth);WebUtils.writeJson(resp,resMap);});//失败的处理filter.setAuthenticationFailureHandler((req,resp,ex) -> {Map<String,Object> resMap = new HashMap<>();resMap.put("code","5001");resMap.put("msg",ex.getMessage());WebUtils.writeJson(resp,resMap);});// 指定自己的authenticationmanagerfilter.setAuthenticationManager(authenticationManagerBean());return filter;}/***  springsecurity 配置* @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().mvcMatchers("/comm/kaptcha.jpg").permitAll()// 该路径放行.mvcMatchers("/").permitAll()// 入口页放行.anyRequest().authenticated()// 所有请求都需要认证.and().formLogin()// 表单配置.loginPage("/").and().csrf().disable();//关闭csrf 防护// 定义登录图形过滤器,替换掉UsernamePasswordAuthenticationFilterhttp.addFilterAt(loginVerifyImgFilter(), UsernamePasswordAuthenticationFilter.class);}

3.2 图片验证码base生成

@RestController
@Slf4j
public class CommController {/*** 获取图形验证码* @param session* @param response* @return*/@RequestMapping("/comm/kaptcha.jpg")public Map<String,String> image(HttpSession session, HttpServletResponse response) {// 自定义纯数字的验证码(随机4位数字,可重复)RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(92, 40,4,10);lineCaptcha.setGenerator(randomGenerator);// 重新生成codelineCaptcha.createCode();// 获取String captchaCode = lineCaptcha.getCode();log.info("获取到验证码为:{}",captchaCode);session.setAttribute(ComConst.SESSION_CAPTCHA,captchaCode);// 转为base64String imageBase64 = lineCaptcha.getImageBase64();HashMap<String, String> resMap = MapUtil.newHashMap();resMap.put("code","0000");resMap.put("data",imageBase64);return resMap;}}

3.2.1  postman 效果

一般由后台将图片转为base64后,前端再通过传过来的base64 评价 image/ 到 img标签的src 就可以显示出来;需要加上前缀:data:image/jpeg;base64, 后面再把返回的data中的结果拼接到后面

3.3 验证码过滤器(核心类)

该过滤器需要加到配置security 配置里面,用来替换到默认的 UsernamePasswordAuthenticationFilter 过来器,所以之前配置的

formLogin.loginPage("/") 
.loginProcessingUrl("/doLogin")    //form表单提交地址(POST)
//.defaultSuccessUrl("/main",true)    //登陆成功后跳转的页面,也可以通过Handler实现高度自定义
.successForwardUrl("/main")
这些配置实际都会失效
public class LoginVerifyCaptchaFilter extends UsernamePasswordAuthenticationFilter {private String imageParams = "verifyImg";@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (!request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}Map<String,String> userMap = null;try {// 用户信息userMap = new ObjectMapper().readValue(request.getInputStream(), Map.class);} catch (Exception e) {e.printStackTrace();}// 获取图形验证码String reqImgCode = userMap.get(getImageParams());String username = userMap.get(getUsernameParameter());String password = userMap.get(getPasswordParameter());// 获取session 的验证码String realCode = (String)request.getSession().getAttribute(ComConst.SESSION_CAPTCHA);// 图形验证码通过if (!ObjectUtils.isEmpty(reqImgCode) && !ObjectUtils.isEmpty(realCode) && realCode.equalsIgnoreCase(reqImgCode)) {UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);// Allow subclasses to set the "details" propertysetDetails(request, authRequest);return this.getAuthenticationManager().authenticate(authRequest);}throw new CustomerException("图形验证码错误!");//  return super.attemptAuthentication(request,response);}public String getImageParams() {return imageParams;}public void setImageParams(String imageParams) {this.imageParams = imageParams;}
}

3.4 新建一个测试类 

@RestController
public class HelloController {@RequestMapping("hello")public String hello() {return "hello web security ";}}

3.5 验证结果

我们看到 hello 接口是受到保护的,没有认证是访问不了的

3.5.1  访问hello接口

这个时候登录成功后再将登录接口返回的cookie 信息放到hello接口中请求

3.6 增加异常处理

该地方是用来处理用户未登录,接口提示需要用户有认证信息,这个时候我们没有登录访问受限接口 hello 就会提示,请认证后再来请求接口,新增一个工具类,用于将写出json数据

/*** 写出json 数据** @param response* @throws Exception*/
public static void writeJson(HttpServletResponse response, Object object) {response.setContentType("application/json;charset=UTF-8");response.setCharacterEncoding("UTF-8");response.setHeader("Cache-Control", "no-cache");PrintWriter pw = null;try {pw = response.getWriter();pw.print(JSONUtil.toJsonStr(object));pw.flush();} catch (IOException e) {e.printStackTrace();} finally {if (pw != null) {pw.close();}}
}
.authenticationEntryPoint(((request, response, authException) -> {// 判断是否有登录Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication == null) {WebUtils.writeJson(response,"请认证后再来请求接口");} else {WebUtils.writeJson(response,authException.getLocalizedMessage());}}))

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

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

相关文章

微信小程序知识点(一)

1.条件判断&#xff1a; wx:if&#xff0c;wx:elif&#xff0c;wx:else 和Hidden的区别 wx:if等是动态实现组件的&#xff0c;符合条件&#xff0c;页面上就新增一个组件&#xff0c;不符合&#xff0c;就会在也页面上加载&#xff0c;而Hidden只是控制页面的组件的显示与否&…

CUDA 计算点集与点集之间的距离

文章目录 一、简介二、实现代码三、实现效果参考资料 一、简介 这里使用CUDA实现一种计算计算点集与点集之间的距离的方法&#xff0c;其思路很简单&#xff0c;就是计算每个点到另一个点集之间的最小距离&#xff0c;最终保存结果到一个数组中&#xff0c;通过这种方式可以快速…

海力士A-DIE颗粒内存条震撼发布:毁灭者星际战舰DDR5内存条登场

**海力士A-DIE颗粒内存条震撼发布&#xff1a;毁灭者星际战舰内存条登场** 近日&#xff0c;海力士正式发布了全新一代A-DIE颗粒内存条——毁灭者星际战舰DDR5 7200RGB电竞内存条。这款内存条凭借其卓越的性能和先进的技术&#xff0c;成为数码爱好者关注的焦点。 导语&#xf…

分类预测|基于麻雀优化核极限学习机的数据分类预测Matlab程序SSA-KELM 多特征输入多类别输出 含基础KELM

分类预测|基于麻雀优化核极限学习机的数据分类预测Matlab程序SSA-KELM 多特征输入多类别输出 含基础KELM 文章目录 前言分类预测|基于麻雀优化核极限学习机的数据分类预测Matlab程序SSA-KELM 多特征输入多类别输出 含基础KELM 一、SSA-KELM模型SSA-KELM 分类预测的详细原理和流…

剑侠情缘c#版(游戏源码+资源+工具+程序),百度云盘下载,大小1.68G

剑侠情缘c#版&#xff08;游戏源码资源工具程序&#xff09;&#xff0c;c#开发的&#xff0c;喜欢研究游戏的可以下载看看。亲测可进游戏。 剑侠情缘c#版&#xff08;游戏源码资源工具程序&#xff09;下载地址&#xff1a; 通过网盘分享的文件&#xff1a;【游戏】剑侠情缘c#…

U-Mail垃圾邮件过滤网关‍是如何过滤垃圾邮件的?

随着互联网的普及&#xff0c;垃圾邮件已经成为计算机网络安全的又一个公害。因此&#xff0c;反垃圾邮件已经成为互联网应用研究中一个重要课题。为了防止垃圾邮件首先要学会保护自己的邮件地址&#xff0c;避免在网上随意登记和使用邮件地址&#xff0c;预防垃圾邮件骚扰。其…

Mysql——高可用集群部署

目录 一、源码编译mysql 二、mysql的主从复制 2.1、主从复制 2.2、延迟复制 2.3、慢查询日志 2.4、MySQL的并行复制 三、MySQL半同步模式 四、mysql高可用组复制 五、mysql-router 六、mysql高可用MHA 七、为MHA添加VIP功能 一、源码编译mysql 1、安装依赖 [rootm…

Pyqt5高级技巧:多线程任务、窗体交互、常用控件介绍(含基础Demo)

目录 一、多线程任务和多窗体交互 二、增删改查Demo 三、UI设计 【css效果代码对照表】 【实现效果】 【实现代码】 【常见问题】 Q1&#xff1a;工具栏怎么加&#xff0c;资源图片怎么加 Q2&#xff1a;控件被背景染色怎么办&#xff1f; Q3&#xff1a;QTdesigner有…

磐石云语音识别引擎

磐石云发布了V1.2.2版本语音识别引擎。 经过严格客观的测试识别效果和阿里云、讯飞、火山进行了对比几乎无差。&#xff08;欢迎对比测试&#xff09; 上图是CPU下的流式识别效果 RTF0.1~0.14,也就是一并发一个小时大约处理7~10小时&#xff0c;这取决于硬件的配置&#xff0…

MathType常见问题汇总

文章目录 MathType常见问题汇总一、如何将MathType内嵌到WPS工具栏中&#xff1f;二、在word中&#xff0c;如何批量修改所有MathType公式的字体以及大小格式&#xff1f;三、如何解决插入MathType公式后的行间距发生改变&#xff1f;参考 MathType常见问题汇总 一、如何将Mat…

Altium designer设计经验谈——常用规则的使用(二)

文章目录 前言三、规则设置介绍——走线规则1、Routing——>Width 线宽2、Routing——>Topology 拓扑 四、规则设置介绍——平面层规则1、Plane——>电源层连接样式 Power Plane Connect Style2、Plane——>电源层间距距离 Power Plane Clearance3、Plane——>多…

Oracle rac模式下undo表空间爆满的解决

文章目录 前言一、确认对应实例的undo表空间二、确认对应实例undo的文件位置三、确认回滚段使用情况四、检查undo segment状态五、创建新的undo表空间并进行切换六、等待原undo表空间segment状态变更为offline七、删除原undo表空间以及数据文件 前言 一、确认对应实例的undo表空…

2.【R语言】RStudio的下载和安装

2.1 RStudio的介绍 RStudio 是一种集成开发环境 (Integrated Development Environment, IDE)&#xff0c;主要用于 R 语言的开发和数据分析。它为 R 语言的使用者提供了一系列便捷的工具和功能&#xff0c;使得编写、调试和执行 R 代码变得更加高效和直观。以下是对 RStudio 主…

计算机毕业设计 | SpringBoot+vue移动端音乐网站 音乐播放器(附源码)

1&#xff0c;项目背景 随着计算机技术的发展&#xff0c;网络技术对我们生活和工作显得越来越重要&#xff0c;特别是现在信息高度发达的今天&#xff0c;人们对最新信息的需求和发布迫切的需要及时性。为了满足不同人们对网络需求&#xff0c;各种特色&#xff0c;各种主题的…

【GeoSceneDatastore】连接失败

报错信息 解决方案 删除环境变量tomcat的配置信息&#xff08;CATALINA_BASE、CATALINA_HOME、CATALINA_TMPDIR&#xff09;&#xff0c;重新启动Datastore服务

音频检测电路 | 声音传感器模块 | 口哨开关 | Arduino

音频检测电路 | 声音传感器模块 | 口哨开关 | Arduino 案例分析电路设计1. **基本音频检测电路设计**电路结构:2. **灵敏度调节原理**方法:3. **非 MCU 控制的 LED 触发**设计步骤:4. **电路示例**5. **示意图(文本描述)**总结实验方法案例分析 一个硅胶娃娃,挤压或拍打…

NoSQL:数据库领域的“新潮力量”——从起源到未来的全面解析

引言 曾几何时&#xff0c;关系型数据库&#xff08;RDBMS&#xff09;就是数据管理的“老大哥”&#xff0c;一统江湖&#xff0c;所向披靡。然而&#xff0c;随着大数据时代的到来&#xff0c;数据量像火箭般飙升&#xff0c;数据的形态也变得越来越“随性”&#xff0c;传统…

高职院校大数据分析与可视化微服务架构实训室解决方案

一、前言 随着信息技术的飞速发展&#xff0c;大数据已成为推动社会进步与产业升级的关键力量。为了培养适应未来市场需求的高素质技术技能型人才&#xff0c;高职院校纷纷加大对大数据分析与可视化技术的教学投入。唯众&#xff0c;作为国内领先的职业教育解决方案提供商&…

java编辑器——IntelliJ IDEA

java编辑器有两种选择——IntelliJ IDEA和VsCode。其中IntelliJ IDEA现在是企业用的比较多的&#xff0c;是专门为java设计的&#xff0c;而VsCode则是通过插件来实现Java编辑的。 1.IntelliJ IDEA 官网下载链接&#xff1a;https://www.jetbrains.com/idea/ 注意选择社区版…

软考高级:系统架构设计师——软件架构设计 Chapter 笔记

软考高级&#xff1a;系统架构设计师——软件架构设计 1 软件架构设计—基本概念架构所处的位置架构发展历程架构的“41”视图例题 架构描述语言&#xff08;ADL&#xff09;例题 2软件架构设计—架构风格数据流风格调用/返回 风格独立构件风格虚拟机风格仓库风格&#xff08;以…