自学内容网 自学内容网

SpringBoot企业级开发(SpringSecurity安全控制+pringBatch批处理+异步消息+系统集成SpringIntegration)

Spring Security

多个过滤器来实现所有安全的功能,只需要注册一个特殊的DelegatingFilterProxy过滤器到WebAppliationInitializer即可
实际使用中需要让自己的Initializer类继承AbstractSecurity WebApplicationInitializer抽象类即可。

AbstractSecurityWebApplicationInitializer实现了WebApplicationInitializer接口,并通过onStartup方法调用

①、依赖

spring-boot-starter-data-jpa
spring-boot-starter-security
spring-boot-starter-thymeleaf
ojdbc6
thymeleaf-extras-springsecurity4

②、配置

spring.datasource.drivuerClassName=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521\:xe
spring.datasource.username=boot
spring.datasource.password=boot

logging.level.org.springframework.security=INFO
spring.thymeleaf.cache=false

spring.jpa.hibernate.ddl-auto=update #自动生成用户表、角色表以及关联表
spring.jpa.show-sql=true

将bootstrap.min.css放置在src/main/resources/static/css下,此路径默认不拦截

③、用户和角色

@Entity
public class SysUser implements UserDetails{//实现该接口,用户实体即为Spring Security使用的用户

private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;

private String username;
private String password;


@ManyToManay(cascade = {CascadeType.REFRESH},fetch=FetchType.EAGER)
private List<SysRole> roles;

@Override
public Colletion<? extends GrantedAuthority> getAuthorities(){//将用户的角色作为权限

List<GrantedAuthority> auths = new ArrayList<>();
List<SysRole> roles = this.getRoles();
for(SysRole role:roles){
auths.add(new SimpleGrantedAuthority(role.getName()));
}
return false;
}

@Override
public boolean isAccountNonExpired(){
return true;
}

@Override
public boolean isAccountNonLocked(){
return true;
}

@Override
public boolean isCredentialsNonExpired(){
return true;
}

@Override
public boolean isEnabled(){
return true;
}

//省略get\set方法
}
@Entity
public class SysRole{

@Id
@GeneratedValue
private Long id;

private String name;

//省略getter、setter方法
}

④、Dao数据访问

public interface SysUserRepository extends JpaRepository<SysUser,Long>{

SysUser findByUsername(String username);
}

⑤、自定义Service服务

public class CustomUserService implements UserDetailsService{//自定义需要实现该接口

@Autowired
SysUserRepository userRepository;

@Override
public UserDetails loadUserByUsername(){//获得用户,直接返回给SprngSecurity
SysUser user = userRepository.findUsername(username);
if(user == null){
throw new UsernameNotFoundException("用户名不存在");
}
return user;
}
}

⑥、Configuration配置SpringMVC

注册访问/login转向login.html

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{

//访问http://localhost:8080 将会自动跳转到登录页面
@Override
public void addViewController(ViewControllerRegistry registry){
registry.addViewController("/login").setViewName("login");
}
}

扩展Spring Security配置,所有请求需要认证登录后才能方法(登录和注销)

SpringBoot针对Spring Security的自动配置通过SecurityAutoConfiguration和SecurityProperties来配置

SpringBoot做了很多配置,需要扩展配置只需要继承WebSecurityConfigurerAdapter类,无需@EnableWebSecurity注解

//相关的安全配置
@Configuration
//@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

@Bean
UserDetailsService customUserService(){//注册该bean
return new CustomUserService();
}

/**
用户认证
①、添加内存中的用户,并且可以给用户指定角色权限
auth.inMemoryAuthentication().withUser("wyf").password("wyf")
.roles("ROLE_ADMIN").and().withUser("wisely").password("wisely").roles("ROLE_USER");

②、JDBC中的用户直接指定dataSource即可
这里的SpringSecurity默认了数据库接口,通过jdbcAuthentication源码可以得出JdbcDaoImpl中定义了默认的用户及角色权限

@Autowired
DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception{
auth.jdbcAuthentication().dataource(dataSource);
//也可以i定义查询用户和权限的SQL
auth.jdbcAuthentication().dataource(dataSource)
.usersByUsernameQuery("select username,password,true from myusers where username=?")
.authoritiesByUsername("select username,role from roles where username=?");
}

*/
//③、通用用户,实现UerDetailsService接口,以上JDBC用户和内存用户就是其实现
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception{
auth.userDetailsService(customUserServie());//添加自定义认证,注册
}

/**
请求授权,匹配了一下请求路径,需要针对当前用户的信息对请求路径进行安全处理
Ⅰ、antMatchers 使用Ant风格的路径皮喷
Ⅱ、regexMatchers 使用正则表达式匹配路径
Ⅲ、anyRequest 匹配所有路径

安全处理方法:
access(String) SpringEL表达式结果为true时可访问
anonymous() 匿名访问
denyAll() 用户不能访问
fullyAuthenticated() 用户完全认证可访问(非remeber me下自动登录)
hasAnyAuthority(String..) 如果用户有参数,则其中任一权限可访问
hasAnyRole(String..) 如果用户有参数,则其中任一角色可访问
hasAuthority(String) 如果用户有参数,则其权限可访问
hasIpAddress(String) 如果用户来自参数中的IP则可访问
hasRole(String) 若用户有参数中的角色可访问
permitAll() 用户可任意访问
rememberMe() 运行通过remember-me登录的用户访问
authenticated() 用户登录后可访问

http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ROLE_ADMIN")
.antMatchers("/user/**").hasAnyRole("ROLE_ADMIN","ROLE_USER")
.anyRequst().authenticated(); //其余所有请求都需要认证登录后才可以访问
*/
@Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()//开始请求权限配置
.anyRequest().authenticated()//所有请求需要认证登陆后才能访问
.and()
.formLogin() //定制登录操作
.loginPage("/login")//登录页面的访问地址
.defaultSuccessUrl("/index")//指定登录成功后转向的页面
.failureUrl("/login?error")//登录失败转向的页面
.permitAll()//定制登录行为,登录页面可以任意访问
.and()
.rememberMe()//开启cookie存储用户信息
.tokenValiditySeconds(1209600)//指定cookie有效期2个星期
.key("myKey")//指定cookie中的私钥
.and()
.logout()
.logout("/costom-logout")//指定注销的URL路径
.logoutSuccessUrl("/logout-success")//指定注销成功后转向的页面
.permitAll();//定制注销行为,注销请求可任意访问
}
}

⑦、页面

<!--Thymeleaf提供了Spring Security的标签支持-->
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">

获得当前用户名
sec:authentication=“name”

当前用户觉得为ROLE_ADMIN时才现实标签内容
sec:authorize=“hasRole(‘ROLE_ADMIN’)”

注销的默认路径/logout,需要通过POST请求提交
th:action=“@{/logout}” method=“post”

Spring Batch

处理大量数据的框架,用来读取大量数据,然后进行一定处理后输出成指定的形式

①、依赖

spring-boot-starter-jdbc
spring-boot-starter-batch(因为使用Oracle,所以排除hsqldb)
spring-boot-starter-web
ojdbc6
hibernate-validator 数据校验

数据准备:
src/main/resources/people.csv中添加 jordan,23,非汉族,芝加哥 等多条类似的数据
数据表sql
id name age nation address

②、实体类

public class Person{

@Size(max=4,min=2) //使用JSR-303校验数据
private String name;

private int age;

private String nation;

private String address;

//省略get和set方法
}

③、数据处理及校验

public class CsvIntemProcessor extemds ValidatingItemProcessor<Person>{

@Override
public Person process(Person item) throws ValidationException{
super.process(item);//调用自定义校验器
if(item.getNation().equals("汉族")){
item.setNation("01");
}else{
item.setNation("02");
}
return item;
}
}
public class CsvBeanValidator<T> implements Validator<T>,InitializingBean{

private javax.validation.Validator validator;

@Override
public void afterProperitesSet()throws Exception{//使用JSR-303校验数据
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.usingContext().getValidator();
}

@Override
public void validate(T value)throws ValidationException{
//使用Validator的validate的方法校验数据
Set<ConstraintViolation<T>> constraintViolations = validator.validate(value);
if(constraitViolations.size()>0){
StringBuilder message = new StringBuilder();
for(ConstraintVilation<T> constraintViolation : constraintViolations){
message.append(constraintViolation.getMessage() + "\n");
}
throw new ValidationException(message.toString());
}
}
}

④、Job监听

public class CsvJobListener implements JobExecutionListener{

long startTime;
long endTime;

@Override
public void beforeJob(JobExecution jobExecution){
startTime = System.currentTimeMillis();
System.out.println("任务处理开始");
}

@Override
public void afterJod(JobExecution jobExecution){
startTime = System.currentTimeMillis();
System.out.println("任务处理结束");
System.out.println("耗时:"+(endTime-startTime)+"ms");
}
}

⑤、配置

  • JobRepository 用来注册Job容器
  • JobLauncher 用来启动Job的接口
  • Job 实际执行的任务,包含一个或多个Step
  • Step 包含ItemReader ItemProcessor和ItemWriter
  • ItemReader 用来读取数据的接口
  • ItemProcessor 用来处理数据的接口
  • ItemWriter 用来输出数据的接口
@Configuration
@EnableBatchProcessing //开启批处理支持
public class CsvBatchConfig{

@Bean
public ItemReader<Person> reader()throws Exception{

FlatFileItemReader<Person> reader = new FlatFileItemReader<>();//读取文件

reader.setResource(new ClassPathResource("people.csv"));//设置csv文件路径
reader.setLineMapper(new DefautLineMapper<Person>(){
{
setLineTokenizer(new DelimitedLineTokenizer(){
{
setNames(new String[]{"name","age","nation","address"});
}
});

setFieldSetMapper(new BeanWrapperFieldSetMapper<Person>(){
{
setTargetType(Person.class);
}
});
}
});

return reader;
}

@Bean
public ItemProcessor<Person,Person> processor(){
CsvItemProcessor processor = new CsvItemProcessor();//使用自定义的ItemProcessor的实现CsvItemProcessor
processor.setValidator(csvBeanValidator());//为processor指定校验器为CsvBeanValidator
return processor;
}

@Bean
public ItemWriter<Person> write(DataSource dataSource){//SpringBoot让已有的容器bean,注入

JdbcBatchItemWriter<Person> writer = new JdbcBatchItemWriter<>();//使用JDBC批处理的jdbcBatchItemWriter来写数据到数据库
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>());
String sql = "insert into person"+"(id,name,age,nation,address)"+"values(hibernate_sequence.nextval,:name,:age,:nation,:address)";

wirter.setSql(sql);//设置要执行批处理的SQL语句
writer.setDataSource(dataSource);
return writer;
}

@Bean
public JobRepository jobRepository(DataSource dataSource,PlatformTransactionManager transactionManager)throws Exception{

JobRepositoryFactoryBean jobRepositoryFactoryBean = new JopRepositoryFactoryBean();
jobRepositoryFactoryBean.setDataSource(dataSource);
jobRepositoryFactoryBean.setTransactionManager(transactionManager);
jobRepositoryFactoryBean.setDatabaseType("oracle");
return jobRepositoryFactoryBean.getObject();
}

@Bean
public SimpleJobLauncher jobLauncher(DataSource dataSource,PlatformTransactionManager transactionManager){

SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository(dataSource,transactionManager));
return jobLauncher;
}

@Bean
public Job importJob(JobBuilderFactory jobs,Step s1){

return jobs.get("importJob")
.incrementer(new RunIdIncrementer())
.flow(s1)//为Job指定Step
.end()
.listener(csvJobListener())//绑定监听器
.build();
}

@Bean
public Step step1(StepBuilderFactory stepBuilderFactory,ItemReader<Person> reader,
ItemWriter<Person> writer,ItemProcessor<Person,Person> processor){

return stepBuilderFactory
.get("step1")
.<Person,Person>chunk(65000)//批处理,每次提交65000条数据
.reader(reader)//为step绑定那个reader
.processor(processor)//给step绑定processor
.writer(writer)//给step绑定writer
.build();
}

@Bean
public CsvJobListener csvJobListener(){

return new CsvJobListener();
}

@Bean
public Validator<Person> csvBeanValidator(){
return new CsvBeanValidator<Person>();
}
}

⑥、运行

SpringBoot会自动初始化SpringBatch数据库,并将CSV中的数据导入到数据库中
在这里插入图片描述
监听器效果
在这里插入图片描述
数据已导入且做转换处理
在这里插入图片描述
在这里插入图片描述
⑦、手动触发任务

注释调CsvBatchConfig类的@Configuration注解,让此类不在起效

新建TriggerBatchConfig配置类,内容同CsvBatchConfig完全保持一致

修改定义ItemReader

@Bean
@StepScope
public FlatFileItemReader<Person> reader(@Value("#{jobParameters['input.file.name']}") String pathToFile)throws Exception{

FlatFileItemReader<Person> reader = new FlatFileItemReader<>();//
reader.setResource(new ClassPathResource(pathToFile));//
reader.setLineMapper(new DefautLineMapper<Person>(){
{
setLineTokenizer(new DelimitedLineTokenizer(){
{
setNames(new String[]{"name","age","nation","address"});
}
});

setFieldSetMapper(new BeanWrapperFieldSetMapper<Person>(){
{
setTargetType(Person.class);
}
});
}
});

return reader;
}

控制定义

@RestController
public class DemoController{

@Autowired
JobLauncher jobLauncher;

@Autowired
Job importJob;

public JobParameters jobParameters;

@RequestMapping("/imp")
public String imp(String fileName)throws Exception{
String path = fileName + ".csv";
jobParameters = new JobParametersBuilder()
.addLong("time",System.currentTimeMillis())
.addString("input.file.name",path)
.toJobParameters();

jobLauncher.run(importJob,jobParameters);
return "ok";
}
 
}

此时如果关闭SpringBoot为我们自动执行了Job的配置,在application.properties里使用如下代码
spring.batch.job.enabled=false

访问http://localhost:8080/imp?fileName=people可获得相同的数据导入效果
在这里插入图片描述

异步消息

Spring通过@EnableJms @EnableRabbit开启支持

SpringBoot自动开启了@EnableJms @EnableRabbit

JMS

①、安装ActiveMQ

docker run -d -p 61616:61616 -p 8161:8161 cloudesire/activemq

其中61616是消息代理的端口
8161是ActiveMQ管理界面端口 http://localhost:8161打开ActiveMQ的管理界面 账号密码admin/admin

也可以将ActiveMQ内嵌在程序里依赖activemq-broker

②、依赖

spring-boot-starter
spring-jms
activemq-client

application.properties配置ActiveMQ消息代理
spring.activemq.broker-url=tcp://localhost:61616

③、消息定义

public class Msg implements MessageCreator{

@Override
public Message createMessage(Session session) throws JMSException{
return session.createTextMessage("测试消息");
}
}

④、消息发送及目的地

@SpringBootApplication
public class ChApplication implements CommandLineRunner{//SpringBoot提供的接口

@Autowired
JmsTemplate jmsTemplate;//注入SpringBoot配置好的bean

public static void main(String[] args){
SpringApplication.run(ChApplication.class,args);
}

@Override
public void run()throws Exception{
jmsTemplate.send("my-destination",new Msg());//向my-destination目的地发送Msg的消息
}
}

⑤、消息监听

@Component
public class Receiver{

@JmsListener(destination="my-destination")//Spring4.1新特性,监听的目的地,接收消息
public void receiveMesssage(String message){

Systemm.out.println("接收到:<" + message +">");
}
}

⑥、运行
在这里插入图片描述

AMQP

①、安装RabbitMQ

先下载安装erlang http://www.erlang.org/download.html
下载RabbitMQ https://www.rabbitmq.com/download.html

docker run -d -p 5672:5672 -p 15672:15672 rabbitmq:3-management

5672属于消息代理的端口
15672属于管理界面的端口 http://localhost:15672 guest/guest

②、依赖

spring-boot-starter-amqp

SpringBoot默认Rabbit注解为localhost、端口号5672
无需为SpringBoot的application.properties配置RabbitMQ的连接信息

③、发送信息及目的地

@SpringBootApplication
public class ChApplication implements CommandLineRunner{

@Autowired
RabbitTemplate rabbitTemplate;//注入SpringBoot配置好的

public static void main(String[] args){
SpringApplication.run(ChApplication.class,args);
}

@Bean
public Queue wiselyQueue(){
return new Queue("my-queue");//定义目的地队列
}

@Override
public void run(String... args)throws Exception{
//向队列my-queue发送消息
rabbitTemplate.convertAndSend("my-queue","来自RabbitMQ的问候");
}

}

④、消息监听

@Component
public class Receiver{

@RabbitListener(queues = "my-queue")
public void receiveMessage(String message){
System.out.println("Received<" + message + ">");
}
}

⑤、运行

系统集成Spring Integration

解决不同系统之间交互的问题,通过异步消息驱动来达到系统交互时系统之间的松耦合。

①、读取https://spring.io/blog.atom的新闻聚合文件,atom是一种xml文件,且格式是固定的

将读取到的消息通过分类(Category),将消息转到不同的消息通道
将分类为releases和engineering的消息写入磁盘,将分类为news的消息通过邮件发送
在这里插入图片描述

②、依赖

spring-boot-starter-integration
spring-boot-starter-mail

Spring Integration对atom及mail的支持:
spring-integration-feed
spring-integration-mail

②、读取流程(入口类完成)

@Value("https://spring.io/blog.atom")//获得资源
Resource resource

@Bean(name=PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller(){//配置默认的轮询方式
return Pollers.fixedRate(500).get();
}

@Bean
public FeedEntryMessageSource feedMessageSource() throws IOException{//

FeedEntryMessageSource messageSource = new FeedEntryMessageSource(resource.getURL(),"news");
return messageSource;
}

@Bean
public IntegrationFlow myFlow() throws IOException{

return IntegrationFlows.from(feedMessageSource())//流程从from 方法开始
.<SyndEntry,String> route(payload ->payload.getCategories().get(0).getname(),//选择路由,消息体payload类型SyndEntry
mapping ->mapping.channelMapping("releases","releasesChannel")
.channelMapping("engineering","engineeringChannel")//不同分类的值转向不同的消息通道
.channelMapping("news","newsChannel"))
.get();//获得IntegrationFlow实体,配置为Spring的Bean
}

③、release流程

@Bean
public IntegrationFlow releasesFlow(){

return IntegrationFlows.from(MessageChannels.queue("releasesChannel",10)) //从消息通道releasesChannel开始获取数据
.<SyndEntry,String> transform(payload->"《" + payload.getTitle()+"》"+payload.getLink()+getProperty("line.separator"))//数据转换
.handle(Files.outboundAdapter(new File("e:/springblog"))//file出站适配器
.fileExistsMode(FileExistsMode.APPEND)
.charset("UTF-8")
.fileNameGenerator(message -> "releases.txt")
.get())
.get();

}

④、engineering流程

@Bean
pubic IntegratonFlow engineeringFlow(){

return IntegrationFlows.from(MessageChannels.queue("engineeringChannel",10))
.<SyndEntry,String> transform(e->"《"+e.getTitle()+"》"+e.getLink()+getProperty("line.separator"))
.handle(Files.outboundAdapter(new File("e:/springblog"))
.fileExistsMode(FileExistsMode.APPEND).charset("UTF-8")
.fileNameGenerator(message->"engineering.txt").get())
.get();
}

⑤、news流程

@Bean
public IntegrationFlow newsFlow(){

return IntegrationFlows.from(MessageChannels.queue("newsChannel",10))
.<SyndEntry,String> transform(payload->"《"+payload.getTitle()+"》"+payload.getLink()+getProperty("line.separator"))
.enrichHeaders(Mail.headers() //增加消息头信息
.subject("来自Spring的新闻")
.to("wise-man@126.com")
.from("wisely-man@126.com"))//邮件发送的相关信息
.handle(Mail.outboundAdapter("smtp.126.com")//邮件发送的出站适配器
.port(25)
.protocol("smtp")
.credentials("wisely-man@126.com","****")
.javaMailProperties(p->p.put("mail.debug"),false)),
e->e.id("smtpOut"))
.get();
}

⑥、运行

在这里插入图片描述
在这里插入图片描述
release.txt文件内容
在这里插入图片描述
邮箱接收结果
在这里插入图片描述


原文地址:https://blog.csdn.net/usa_washington/article/details/142713559

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!