十二、Spring Boot与任务
1、异步任务
在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应
迟缓的情况,之前大部分都是使用多线程来完成此类任务,springboot中可以用异步任务解决。
两个注解
- @Async在需要异步执行的方法上标注注解
- @EnableAsync在主类上标注开启异步任务支持
开启异步任务之后,当controller层调用该方法会直接返回结果,该任务异步执行
@Service
public class AsyncService {
//告诉Spring这是一个异步方法
@Async
public void hello(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("处理数据中...");
}
}
2、定时任务
项目开发中经常需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息。Spring为我们提供了异步执行
任务调度的方式,提供TaskExecutor、TaskScheduler接口。
两个注解
- @EnableScheduling标注在主类,开启对定时任务支持
- @Scheduled标注在执行的方法上,并执行cron属性
@Service
public class ScheduledService {
/*
* second(秒),minute(分),hour(时),day of month(日),month(月),day of week(周几)
* 0 * * * * MON-FRI
* 【0 0/5 14,18 * * ?】 每天14点整,和18点整,每隔5分钟执行一次
* 【0 15 10 ? * 1-6】 每个月的周一至周六10:15分执行一次
* 【0 0 2 ? * 6L】每个月的最后一个周六凌晨2点执行一次
* 【0 0 2 LW * ?】每个月的最后一个工作日凌晨2点执行一次
* 【0 0 2-4 ? * 1#1】每个月的第一个周一凌晨2点到4点期间,每个整点都执行一次;
* */
//@Scheduled(cron = "0 * * * * MON-MON") 任意
//@Scheduled(cron = "0,1,2,3,4,5 * * * * MON-MON") 枚举
//@Scheduled(cron = "0-10 * * * * MON-MON") 区间
//@Scheduled(cron = "0/5 * * * * MON-MON") 步长
public void hello(){
System.out.println("hello...");
}
}
字段 | 允许值 | 允许的特殊字符 |
---|---|---|
秒 | 0-59 | ,- * / |
分 | 0-59 | ,- * / |
小时 | 0-23 | ,- * / |
日期 | 1-31 | ,- * ? / L W C |
月份 | 1-12 | ,- * / |
星期 | 0-7或者SUN-SAT,0,7是SUN | ,- * ? / L C # |
特殊字符 | 代表含义 |
---|---|
, | 枚举 |
- | 区间 |
* | 任意 |
/ | 步长 |
? | 日/星期冲突匹配 |
L | 最后 |
W | 工作日 |
C | 和calendar联系后计算过的值 |
# | 星期,4#2,第二个星期四 |
3、邮件任务
Springboot自动配置包中MailSenderAutoConfiguration通过@Import注解向容器中导入了MailSenderJndiConfiguration,而MailSenderJndiConfiguration向容器中导入了JavaMailSenderImpl类,我们可以使用该类发送邮件。
配制文件
spring.mail.username=2770371800@qq.com //邮箱用户名
spring.mail.password=sfeyabakupzrddbb //邮箱密码或者授权码
spring.mail.host=smtp.qq.com
spring.mail.properties.mail.smtp.ssl.enable=true
自动注入
@Autowired
JavaMailSenderImpl mailSender;
简单邮件发送
@Test
//发送简单邮件
public void contextLoads() {
SimpleMailMessage message = new SimpleMailMessage();
//设置主题和内容
message.setSubject("通知");
message.setText("你在干啥");
//设置发送方和接收方
message.setTo("15891724250@163.com");
message.setFrom("2770371800@qq.com");
mailSender.send(message);
}
复杂邮件发送
new MimeMessageHelper(message,true),设置multipart=true,开启对内联元素和附件的支持,helper.setText(“xxxx”,true),html=ture,设置content type=text/html,默认为text/plain
@Test
//发送复杂邮件
public void test02() throws Exception {
//1、创建一个复杂的消息邮件
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
//邮件设置
helper.setSubject("通知:下周计划");
helper.setText("<font style='color:red'>下周一回陕西</font>",true);
helper.setTo("15891724250@163.com");
helper.setFrom("2770371800@qq.com");
//上传文件
helper.addAttachment("2018601.jpg",new File("C:\\Users\\ASUS\\Pictures\\Camera Roll\\2018601.jpg"));
helper.addAttachment("2025167.jpg",new File("C:\\Users\\ASUS\\Pictures\\Camera Roll\\2025167.jpg"));
mailSender.send(mimeMessage);
}
十三、Spring Boot与安全
1、安全
应用程序的两个主要区域是“认证”和“授权”(或者访问控制),这两个主要区域是安全的两个目标。身份验证意味着确认您自己的身份,而授权意味着授予对系统的访问权限。
- 认证
身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。系统确定您是否就是您所说的使用凭据。在公共和
专用网络中,系统通过登录密码验证用户身份。身份验证通常通过用户名和密码完成,
- 授权
另一方面,授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)
的完全权限。简单来说,授权决定了您访问系统的能力以及达到的程度。验证成功后,系统验证您的身份后,即可授权您访问
系统资源。
2、Spring Security
Spring Security是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型。他可以实现强大的web安全控制。对于安全控制,我们仅需引入spring-boot-starter-security模块,进行少量的配置,即可实现强大的安全管理。
WebSecurityConfigurerAdapter:自定义Security策略
通过在配置类中继承该类重写configure(HttpSecurity http)方法来实现自定义策略
@EnableWebSecurity:开启WebSecurity模式
在配置类上标注@EnableWebSecurity开启WebSecurity模式
3、Springboot整合security
1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
导入spring security的包之后,默认情况所有应用访问认证授权,默认用户名user,密码为随机生成的uuid,启动时打印在控制台
2、登录/注销
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//根目录允许所有人访问,其他目录都需要对应角色
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("VIP1")
.antMatchers("/level2/**").hasRole("VIP2")
.antMatchers("/level3/**").hasRole("VIP3");
//开启自动配置的登陆功能,效果,如果没有登陆,没有权限就会来到登陆页面
// /login来到登陆页
// 重定向到/login?error表示登陆失败
http.formLogin();
//开启自动配置的注销功能
//向/logout发送post请求表示注销
http.logout();
}
}
此时除了主页,点击其他的页面都会自动跳转到security自动生成的登录页面,/login来到登陆页,重定向到/login?error表示登陆失败;
http.logout()开启自动配置的注销功能,向/logout发送post请求表示注销,需要在欢迎页加上注销表单,默认注销后自动跳转到登录页面,若想改变转发路径,可以通过logoutSuccessUrl(url)设置路径
<form th:action="@{/logout}" method="post">
<input type="submit" value="注销">
</form>
3. 定义认证规则
为了保证密码能安全存储,springboot内置PasswordEncoder对密码进行转码,默认密码编码器为DelegatingPasswordEncoder。在定义认证规则时,我们需要使用PasswordEncoder将密码转码,由于withDefaultPasswordEncoder()并非安全已被弃用,因此仅在测试中使用。
@Bean
public UserDetailsService users() {
//使用默认的PasswordEncoder
User.UserBuilder builder = User.withDefaultPasswordEncoder();
//定义账户用户名、密码、权限
UserDetails user1 = builder.username("zhangsan")
.password("123456")
.roles("VIP1", "VIP2")
.build();
UserDetails user2 = builder.username("lisi")
.password("123456")
.roles("VIP3", "VIP2")
.build();
UserDetails user3 = builder.username("wangwu")
.password("123456")
.roles("VIP1", "VIP3")
.build();
//使用内存保存用户信息
return new InMemoryUserDetailsManager(user1,user2,user3);
}
4.自定义欢迎页
导入依赖
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
引入命名空间
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
根据是否登录显示游客或用户信息
<!-- 未登录显示此div -->
<div sec:authorize="!isAuthenticated()">
<h2 align="center">游客您好,如果想查看武林秘籍 <a th:href="@{/userlogin}">请登录</a></h2>
</div>
<!-- 登录显示此div -->
<div sec:authorize="isAuthenticated()">
<!-- 显示用户名 -->
<h2>尊敬的<span th:text="${#authentication.name}"></span>,您好!您的角色有:
<!-- 显示用户角色 -->
<span th:text="${#authentication.authorities}"></span></h2>
<form th:action="@{/logout}" method="post">
<input type="submit" value="注销">
</form>
</div>
根据角色类型显示信息
<!-- 具有VIP1的角色显示以下div -->
<div sec:authorize="hasRole('VIP1')">
<h3>普通武功秘籍</h3>
<ul>
<li><a th:href="@{/level1/1}">罗汉拳</a></li>
<li><a th:href="@{/level1/2}">武当长拳</a></li>
<li><a th:href="@{/level1/3}">全真剑法</a></li>
</ul>
</div>
5. 自定义登录页/记住我
@Override
protected void configure(HttpSecurity http) throws Exception {
...
//定制登录页
http.formLogin()
.usernameParameter("user") //表单用户名name
.passwordParameter("pwd") //表单密码name
.loginPage("/userlogin"); //定制登陆页路径
...
//开启记住我
http.rememberMe().
rememberMeParameter("rem"); //设置表单记住我name值
}
通过loginPage(url)设置登录页路径后,在定制的登录页发送post url即为登录请求,并设置表单的name属性都为对应值;
通过勾选记住我,session退出后依然能通过cookie保存用户信息,下次免登陆
<form th:action="@{/userlogin}" method="post">
用户名:<input name="user"/><br>
密码:<input name="pwd"><br/>
<input type="checkbox" name="rem">记住我<br>
<input type="submit" value="登陆">
</form>