SpringBoot 企业级简化开发(四)https://developer.aliyun.com/article/1469563
安全框架总结:
学习了Springsecurity和shiro之后,总结出一些学习方法
- 安全框架的核心思想都十分相似,授权,认证,防伪等
- 他们通常都有几个对象,如shiro中的subject,securityManager一样,
- 源码的注释写有方法使用的模版,我们可以通过下载源码去查看注释,
- Springsercurity和shiro的区别,两个我个人认为,除了一个基于Spring之外功能上两者几乎一致
- 使用的感受
- Spring Security基于Spring开发,项目中如果使用Spring作为基础,配合Spring Security做权限更加方便,而Shiro需要和Spring进行整合开发
- 感觉shiro没有类似于Spring Security那样的安全防护
- shiro不需要基于任何框架,依赖性低
- 个人认为:配置的麻不麻烦关键在于项目用不用Spring,我看大神们写博客都说shrio配置要更简单一些,但是简单的上手了两个安全框架之后,我觉得使用了Spring的项目上手security要比shiro简单的多,
- 还有个个人感想,帮助文档的阅读能力太重要了,学习和接触新技术在没有教程的情况下,文档的阅读能力决定了你的学习上限(个人中间有一段只照着官方文档学习,十分痛苦)
- 以上就是安全框架简单上手的全内容啦,
swagger
学习目标:
- 了解Swagger的作用和概念
- 了解前后端分离
- 在SpringBoot中集成Swagger
Swagger简介
故事还是要从前后端分离讲起啊
前后端分离:VUE+SpringBoot 基本上都用这一套
后端时代:前端只用管理静态页面,html===》后端,使用模版引擎 jsp=》后端主力
前后端分离时代:
- 后端:后端控制层,服务层,数据访问层【后端团队】
- 前端:前端控制层,视图层,【前端团队】
- 伪造后端数据,json,已经存在数据,不需要后端,前端工程依旧可以跑起来
- 前后端如何交互 ====》API
- 前后端相对独立,松耦合
- 前后端甚至可以部署在不同的服务器上
产生一个问题:
- 前后端联调,前端和后端人员无法做到及时协商,解决问题,导致问题爆发
- 需要一个东西可以解决这个问题
解决问题:
- 首先指定计划,实时更新API,较低集成风险
- 早些年:指定word计划文档
- 前后端分离:
- 前端测试后端接口:postman
- 后端提供接口,需要使用更新最新的消息及改动!
Swagger:
- 号称世界上最流行的api框架
- Restful Api文档在线自动生成工具==》api文档和api定义开发
- 直接运行,可以在线测试api接口;
- 执行多种语言(c#,java,php)
在项目中使用Swagger需要Springfox
- swagger2
- ui
SpringBoot集成Swagger
- 新建项目:SpringBoot-Swagger
- 导入相关依赖
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>3.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>3.0.0</version> </dependency>
新版(3.0)的直接加入启动器
- 创建一个helloword的项目
- 配置Swagger==>就可以启动看看效果了 3.0版本后不需要在加入@enableopenapi,和@enableswagger2这两个注解,
package com.hyc.springbootswagger.config; import org.springframework.context.annotation.Configuration; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration public class swaggerconfig { }
路径:http://localhost:8080/swagger-ui/index.html
配置Swagger
配置呢,Swagger有自己的实例
我们使用docket来配置swagger的基本信息
@Bean public Docket docket(){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()); } // 配置swagger基本信息 private ApiInfo apiInfo(){ Contact contact = new Contact("xxx", "hyc.com", "3132774018@qq.com"); return new ApiInfo( "XXX的swagger", "签名", "1.0", "hyc.com", contact, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList()); }
应为没有set方法所以我们只能用构造器,貌似,还有一个什么biuder可以使用,有机会去试试
swagger配置扫描接口
select()来设置扫描
扫描接口配置的方法:
apis:
- RequestHandlerSelectors扫描接口的方式
- basePackage指定扫描包
- any()扫描全部
- none()不扫描
- withclassannotation 扫描类的注解(里面必须放注解的反射对象)
path:过滤哪里什么路径
- paths(PathSelectors.ant("/hyc/**"))
.select() //指定我们需要基于什么包扫描 .apis(RequestHandlerSelectors.basePackage("com.hyc.springbootswagger.controller")) .build();
使用了自定义,那么swagger就不会去扫描其他的位置,会扫描你指定的这个报下的请求
可以发现,现在只有controller下的请求才会被扫描
是否开启Swagger
.enable(false)//eanble决定了是否启动swagger
如果为false那我们就无法进入swagger-ui/index.html了
如何让我在测试的时候用swagger,发布的时候不用swagger
environment.acceptsProfiles
来判断是否处在环境中
//配置swagger要使用的环境 Profiles profiles = Profiles.of("dev", "test");
用profiles来配置使用环境
.enable(flag)//eanble判断是否启动swagger
api分组
分组,如何分组,
.groupName("胡宇辰")
分组,如何多个分组?,我有多个docket就可以有多个.groupName
@Bean public Docket docket(Environment environment){ //配置swagger要使用的环境 Profiles profiles = Profiles.of("dev", "test"); //environment。acceptsProfiles判断自己是否在自己设定的环境中 boolean flag = environment.acceptsProfiles(profiles); return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .groupName("胡宇辰") .enable(flag)//eanble决定了是否启动swagger .select() //指定我们需要基于什么包扫描 /*apis * RequestHandlerSelectors扫描接口的方式 * basePackage指定扫描包 * any()扫描全部 * none()不扫描 * withclassannotation 扫描类的注解(里面必须放注解的反射对象) * */ .apis(RequestHandlerSelectors.basePackage("com.hyc.springbootswagger.controller")) /*path:过滤哪里什么路径 * * */ // .paths(PathSelectors.ant("/hyc/**")) .build(); } @Bean public Docket docket1(Environment environment){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .groupName("小刘"); } @Bean public Docket docket2(Environment environment){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .groupName("小郑"); }
配置多个组
就是有很多个docket,
效果:
实体类
只要我们的接口中,有接口返回的是实体类,那么就是会被swagger扫描
我们写一个方法
@PostMapping("/user") public User user(){ return new User(); }
返回的是实体类user,user里有两个字段,name和age
页面效果图:
那我们看到的如@API这些注解是干什么的呢?
Swagger注解
用来解释类的用@Apimodel
@ApiModel("用户信息实体类") public class User{ }
用来解释类中的属性用@ApiModelProperty()
@ApiModelProperty("用户名字") public String name; @ApiModelProperty("用户年龄") public int age;
小疑问:我用private修饰的变量这么写就不显示,怎么办?
解决方案:写在get方法上就可以有效果了
Swagger测试接口
测试接口十分好用,
我们可以测试自己的接口是否有效
小测试:
测试接口:
@PostMapping("/userJY") public User user2(String name,int age){ User user = new User(name,age); return user; }
测试页面步骤图
查看提交后的接口信息
Swagger总结
- Swagger最重大的使命就是使前后端人员之间的和谐关系有所好转
- 接口文档可以实时更新
- 可以在线测试后端接口,这个功能好评,爽的一批
Swagger是一个十分好用的工具,很多公司在使用
PS:处于安全考虑,我们在发布的时候需要关闭Swagger
功能
文件上传
配置:
# 上传文件总的最大值 spring.servlet.multipart.max-request-size=10MB # 单个文件的最大值 spring.servlet.multipart.max-file-size=10MB ## jsp spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp
单个文件上传的示例
@Controller public class UploadController { private static final Logger LOGGER = LoggerFactory.getLogger(UploadController.class); @GetMapping("/upload") public String upload() { return "upload"; } @PostMapping("/upload") @ResponseBody public String upload(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return "上传失败,请选择文件"; } String fileName = file.getOriginalFilename(); String filePath = "/Users/itinypocket/workspace/temp/"; File dest = new File(filePath + fileName); try { file.transferTo(dest); LOGGER.info("上传成功"); return "上传成功"; } catch (IOException e) { LOGGER.error(e.toString(), e); } return "上传失败!"; } }
多个文件上传的示例
@GetMapping("/multiUpload") public String multiUpload() { return "multiUpload"; } @PostMapping("/multiUpload") @ResponseBody public String multiUpload(HttpServletRequest request) { List<MultipartFile> files = ((MultipartHttpServletRequest) request).getFiles("file"); String filePath = "/Users/itinypocket/workspace/temp/"; for (int i = 0; i < files.size(); i++) { MultipartFile file = files.get(i); if (file.isEmpty()) { return "上传第" + (i++) + "个文件失败"; } String fileName = file.getOriginalFilename(); File dest = new File(filePath + fileName); try { file.transferTo(dest); LOGGER.info("第" + (i + 1) + "个文件上传成功"); } catch (IOException e) { LOGGER.error(e.toString(), e); return "上传第" + (i++) + "个文件失败"; } } return "上传成功"; }
任务
异步任务
小案例:我如何解决假如我访问了一个hello页面,页面调用的方法是睡眠3秒,再返回ok,那么再前端页面我就要等待空白页面3秒钟。如何解决?
Springboot中有一个注解叫@async
异步任务
我们要使用他需要在主启动类开启注解支持@enableasync
代码示例
@Service public class AsyncService { @Async public void hello(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("您好"); } }
这段代码会让执行方法的时候网页加载三秒。
控制层
@RestController public class AsyncController { @Autowired AsyncService asyncService; @RequestMapping("/hello") public String hello(){ asyncService.hello(); return "ok"; } }
我们访问hello这个请求,就会等待三秒,之后页面返回ok,
执行顺序是:
发起请求-----》执行方法---》等三秒---》返回ok
我们启动了异步任务,并且将等待方法上加入注解@Aynsc
再次启动的时候,顺序就变成了这样 发起请求-----》执行方法---》等三秒(同时)---》等待完毕
---》返回ok(同时)
定时任务
gogogo函数接口
第一个接口:TaskExecutor // 任务执行者 第二个接口:TaskScheduler// 任务调度者 注解 @EnableScheduling//开启定时任务功能注解 @Scheduled
使用定时任务首先我们要在主启动类上添加注解开启定时任务:
@EnableScheduling//开启定时任务功能注解
之后就可以通过@Scheduled
来设置cron表达式实现定时任务了
cron表达式
计划任务,是任务在约定的时间执行已经计划好的工作,这是表面的意思。在Linux中,我们经常用到 cron 服务器来完成这项工作。cron服务器可以根据配置文件约定的时间来执行特定的任务。
一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。
按顺序依次为
秒(0~59)
分钟(0~59)
小时(0~23)
天(月)(0~31,但是你需要考虑你月的天数)
月(0~11)
天(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)
年份(1970-2099)
字段 允许值 允许的特殊字符
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日期 1-31 , - * ? / L W C
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * ? / L C #
年(可选) 留空, 1970-2099 , - * /
PS:
- ?只能用在日期和星期,月上
- 在使用“L”参数时,不要指定列表或范围,因为这会导致问题
测试小例子:
@Service public class ScheduledService { //需求:在一定的时间执行这个方法 方法千百种 //cron表达式 @Scheduled(cron = "0 14 10 * * ?") public void hello(){ System.out.println("你被执行了"); } }
我想在每天的10点14分的时候,打印你被执行了,
邮件任务
首先要去qq邮箱开启服务
spring配置文件中的邮件配置
spring.mail.username=3132774018@qq.com spring.mail.password=vymozuqhwxmadhae spring.mail.host= smtp.qq.com #开启加密验证 spring.mail.properties.mail.stmp.ssl.enable=true
spring中关于邮件的实现类:JavaMailSenderImpl
他是一个bean所以我们可以拿来使用
@Autowired JavaMailSenderImpl sender;
简单的邮件发送
@SpringBootTest class Spring8TestApplicationTests { @Autowired JavaMailSenderImpl sender; @Test void contextLoads() { //一个简单的邮件的发送 SimpleMailMessage message = new SimpleMailMessage(); //标题 message.setSubject("你好啊,hyc组内的成员,看到这条消息的时候 hyc还在为了大厂努力"); //内容 message.setText("冲进大厂没有什么是不可以的,好好努力,一切都在掌握之中"); //发送给谁 message.setTo("2549273958@qq.com"); //谁来发送 message.setFrom("3132774018@qq.com"); sender.send(message); } }
复杂邮件,带有附件,html,
我们这里使用sender.createMimeMessage
的方式来创建复杂的邮件发送对象
springboot给我们提供了一个帮助类,MimeMessageHelper
我们用这个helper对象来操作MImeMessage
,
就可以完成复杂邮件发送了
@Test void contextLoads1() throws MessagingException { //复杂的邮件的发送 MimeMessage mimeMessage = sender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true); helper.setSubject("hyc-plus"); helper.setText("<p style='color:red'>你好</p>",true); //附件 helper.addAttachment("1.jpg",new File("D:\\java工程师\\SpringBoot\\spring-8-test\\src\\main\\resources\\public\\1600837482167.jpg")); //发送给谁 helper.setTo("3132774018@qq.com"); //谁来发送 helper.setFrom("3132774018@qq.com"); sender.send(mimeMessage); }
邮件发送工具类
话不多说都在码里面了
MailsenderUtils:
@Component public class MailsenderUtils { @Autowired private JavaMailSenderImpl mailSender; /** * * @param subject :邮件标题 * @param text 邮件内容 * @param html 是否开启html, * @param to 发送给谁 * @param from 谁来发送 * @param filename 附件名字 * @param acc 附件路径 * @return * 1 :发送成功 * -1:发送失败 */ public int MailsenderbyHyc(String subject, String text, boolean html, String to, String from,String filename, File acc){ //复杂的邮件的发送 MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper helper = null; try { helper = new MimeMessageHelper(mimeMessage,true); helper.setSubject(subject); helper.setText(text,html); //附件 helper.addAttachment(filename,acc); //发送给谁 helper.setTo(to); //谁来发送 helper.setFrom(from); mailSender.send(mimeMessage); return 1; } catch (MessagingException e) { e.printStackTrace(); return -1; } } }