任务
异步任务
在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在 处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用 多线程来完成此类任务,其实,在Spring 3.x之后,就已经内置了**@Async**来完 美解决这个问题。
两个注解: @EnableAysnc、@Aysnc
上代码
启动类代码
@EnableAsync
@SpringBootApplication
public class Demo13TaskApplication {
public static void main(String[] args) {
SpringApplication.run(Demo13TaskApplication.class, args);
}
}
service代码
@Service
public class AsyncService {
/*
告诉spring这是一个异步方法
*/
@Async
public void hello() throws InterruptedException {
Thread.sleep(3000);
System.out.println("数据处理中");
}
}
controller代码
@Controller
public class AsyncController {
@Autowired
AsyncService asyncService;
@ResponseBody
@GetMapping("/hello")
public String hello() throws InterruptedException {
asyncService.hello();
return "success";
}
}
只要我们在浏览器中访问hello浏览器会立刻跳转success,服务器会延时打印数据处理
定时任务
项目开发中经常需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前 一天的日志信息。Spring为我们提供了异步执行任务调度的方式,提供 TaskExecutor 、TaskScheduler 接口。
两个注解:@EnableScheduling、@Scheduled
启动类
@EnableScheduling
@SpringBootApplication
public class Demo13TaskApplication {
public static void main(String[] args) {
SpringApplication.run(Demo13TaskApplication.class, args);
}
}
service代码
@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-SAT")
// @Scheduled(cron = "0,1,2,3,4 * * * * MON-SAT")
// @Scheduled(cron = "0-4 * * * * MON-SAT")
@Scheduled(cron = "0/4 * * * * MON-SAT") //每4秒执行一次
public void hello(){
System.out.println("hello....");
}
}
只要我们启动了springboot程序,hello就会每四秒打印一次.
邮件任务
pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
我们要使用的邮箱一定要在这里获取一下密码
配置文件
spring.mail.username=2590416618@qq.com
spring.mail.password=krfyvrmx
spring.mail.host=smtp.qq.com
spring.mail.properties.mail.smtp.ssl.enable=true
测试一下
@SpringBootTest
class Demo13TaskApplicationTests {
@Autowired
JavaMailSenderImpl javaMailSenderImpl;
@Test
void contextLoads() {
//1.创建一个简单的的消息邮件
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setSubject("通知");
simpleMailMessage.setText("这是通过spring-boot进行邮件通知测试");
simpleMailMessage.setTo("2590416618@qq.com");
simpleMailMessage.setFrom("2590416618@qq.com");
javaMailSenderImpl.send(simpleMailMessage);
}
@Test
void test01() throws Exception{
//1.创建一个复杂的消息邮件
MimeMessage mimeMessage = javaMailSenderImpl.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("通知");
helper.setText("<b style='color:red'>这是通过spring-boot进行复杂邮件通知测试</b>",true);
helper.setTo("2590416618@qq.com");
helper.setFrom("2590416618@qq.com");
helper.addAttachment("1.jpg",new File("C:\\Users\\86157\\Desktop\\小丫头\\1.jpg"));
javaMailSenderImpl.send(mimeMessage);
}
}
安全
在spring的安全配置中主要使用的是Spring Security这个框架
如果Spring Security在类路径上,则默认情况下 Web 应用程序是安全的。Spring Boot 依赖 Spring Security 的内容协商策略来确定是否httpBasic
使用formLogin
. 要为 Web 应用程序添加方法级别的安全性,您还可以添加@EnableGlobalMethodSecurity
所需的设置。更多信息可以在Spring Security Reference Guide中找到。
默认UserDetailsService
只有一个用户。用户名为user
,密码是随机的,在应用程序启动时打印在 INFO 级别,如下例所示:
使用生成的安全密码:78fa095d-3f4c-48b1-ad50-e24c31d5cf35
如果您微调日志配置,请确保将org.springframework.boot.autoconfigure.security
类别设置为日志INFO
级别消息。否则,不会打印默认密码。
您可以通过提供spring.security.user.name
和来更改用户名和密码spring.security.user.password
。
web安全
默认安全配置在SecurityAutoConfiguration
和中实现UserDetailsServiceAutoConfiguration
。 为 Web 安全SecurityAutoConfiguration
导入并配置身份验证,这在非 Web 应用程序中也相关。要完全关闭默认的 Web 应用程序安全配置或组合多个 Spring Security 组件(例如 OAuth2 客户端和资源服务器),请添加一个类型的 bean (这样做不会禁用配置或执行器的安全性)。
Config文件
@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");
//开启登录功能
http.formLogin()
.loginPage("/userlogin");
//1、/login来到登陆页
//2、重定向到/login?error表示登陆失败
//3、更多详细规定
//4、默认post形式的 /login代表处理登陆
//5、一但定制loginPage;那么 loginPage的post请求就是登陆
//开启自动配置的登出功能
http.logout().logoutSuccessUrl("/");//注销成功之后来到首页
//1.访问/logout表示用户注销
//开启记住我的功能
http.rememberMe().rememberMeParameter("remeber");
}
//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())
.withUser("zhangsan").password("123456").roles("VIP1","VIP2")
.and().withUser("lisi").password("123456").roles("VIP1")
.and().withUser("wangwu").password("123456").roles("VIP3");
}
}
由于在spring5中密码必须要加入加密策略
我们的加密策略,需要实现PasswordEncoder
@Component
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(charSequence.toString());
}
}
controller
@Controller
public class KungfuController {
private final String PREFIX = "pages/";
/**
* 欢迎页
* @return
*/
@GetMapping("/")
public String index() {
return "welcome";
}
/**
* 登陆页
* @return
*/
@GetMapping("/userlogin")
public String loginPage() {
return PREFIX+"login";
}
/**
* level1页面映射
* @param path
* @return
*/
@GetMapping("/level1/{path}")
public String level1(@PathVariable("path")String path) {
return PREFIX+"level1/"+path;
}
/**
* level2页面映射
* @param path
* @return
*/
@GetMapping("/level2/{path}")
public String level2(@PathVariable("path")String path) {
return PREFIX+"level2/"+path;
}
/**
* level3页面映射
* @param path
* @return
*/
@GetMapping("/level3/{path}")
public String level3(@PathVariable("path")String path) {
return PREFIX+"level3/"+path;
}
}
连接数据库
这是我们的数据库
案例还是我们的案例
这次我们连接数据库用到的是jdbc加mybatis
导入依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
配置类
@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 定制授权规则
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("USER")
.antMatchers("/level2/**").hasRole("ADMIN")
.antMatchers("/level3/**").hasRole("King");
//开启登录功能
http.formLogin()
.loginPage("/userlogin");
//1、/login来到登陆页
//2、重定向到/login?error表示登陆失败
//3、更多详细规定
//4、默认post形式的 /login代表处理登陆
//5、一但定制loginPage;那么 loginPage的post请求就是登陆
//开启自动配置的登出功能
http.logout().logoutSuccessUrl("/");//注销成功之后来到首页
//1.访问/logout表示用户注销
//开启记住我的功能
http.rememberMe().rememberMeParameter("remeber");
}
@Bean
UserLoginServiceImpl UserService() { // 注册UserDetailsService 的bean
return new UserLoginServiceImpl();
}
//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/*auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())
.withUser("zhangsan").password("123456").roles("USER")
.and().withUser("lisi").password("123456").roles("ADMIN")
.and().withUser("wangwu").password("123456").roles("King");*/
auth.userDetailsService(UserService())
.passwordEncoder(new MyPasswordEncoder());
}
}
一定要用这样的形式
@Bean
UserLoginServiceImpl UserService() { // 注册UserDetailsService 的bean
return new UserLoginServiceImpl();
}
new的话会出错,刚开始以为是mybatis配置出错,但是在另外一个界面中却是没有错。仔细查看了报错信息发现报了空指针异常后,然后才进行了数据库的连接操作。感觉可能是bean加载的地方出错,查阅了相关资料了,在下面的博主的文章中:https://www.cnblogs.com/shamo89/p/8534580.html得到启发,拦截器运行时候,处于SpringMVC阶段,@Service应该是还没有注入到spring中,在Spring Security配置文件中将原来的代码改回去。问题得到解决。猜测这个应该是在项目运行使用拦截器的时候,提前在spring注入拦截器需要的组件。如果理解有错,希望大佬留言指正!
loginservice类
@Service
public class UserLoginServiceImpl implements UserLoginService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userinfo = null;
userinfo = userDao.findByUsername(username);
System.out.println(userinfo.getUsername());
User user = new User(userinfo.getUsername(), userinfo.getPassword(), userinfo.getStatus() == 0 ? false : true, true, true, true, getAuthority(userinfo.getRoles()));
return user;
}
private List<SimpleGrantedAuthority> getAuthority(List<Role> roles) {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleName()));
}
return authorities;
}
}
User类
@Date
public class UserInfo {
private String id;
private String username;
private String email;
private String password;
private String phoneNum;
private int status;
private String statusStr;
private List<Role> roles;
}
权限类
public class Role {
private String id;
private String roleName;
private String roleDesc;
private List<Permission> permissions;
private List<UserInfo> users;
}
UserDao类
@Component
@Mapper
public interface UserDao {
@Select("select * from users where username = #{username}")
@Results(
@Result(property = "roles",column = "id",javaType = List.class,many = @Many(select = "com.example.demo14sercurity.Mapper.RoleDao.findByUserId"))
)
public UserInfo findByUsername(String username);
}
RoleDao类
@Component
@Mapper
public interface RoleDao {
@Select("select * from role where id in (select roleId from users_role where userId = #{userId}) ")
public List<Role> findByUserId(String userId);
}
这样我们的数据就连接上我们的安全框架了