springboot整合security和rabbitmq

1.springboot整合security

1.1介绍

Spring Security是Spring、家族中的成员。基于Spring框架,提供了一套 Web 应用

安全性的完整解决方案。

主要功能:认证(Authentication)和授权(Authorization)

用户认证指的是:系统通过用户提供的用户名和密码实现认证。

用户授权指的是:系统判断当前用户是否对某个角色具有操作权限。

1.2导入security的依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

也可以再创建springboot工程的时候直接选择security。

1.3配置信息

1.3.1固定密码

再yml1文件中或者propotise文件中配置:

spring.security.user.name=user
spring.security.user.password=123456

然后再配置config配置类:

@Configuration
public class SecurityConfig{
    /*@Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception{
        return httpSecurity.build();
    }*/
    @Bean
    PasswordEncoder passwordEncoder() {
        String encodingId = "bcrypt";
        Map<String, PasswordEncoder> encoders = new HashMap();
        encoders.put(encodingId, new BCryptPasswordEncoder(12));
        encoders.put("ldap", new LdapShaPasswordEncoder());
        encoders.put("MD4", new Md4PasswordEncoder());
        encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
        encoders.put("noop", NoOpPasswordEncoder.getInstance());
        encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
        encoders.put("scrypt", new SCryptPasswordEncoder());
        encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
        encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
        encoders.put("sha256", new StandardPasswordEncoder());
        encoders.put("argon2", new Argon2PasswordEncoder());
        return new DelegatingPasswordEncoder(encodingId, encoders);
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("============1=============");
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        auth
                .inMemoryAuthentication()
                .withUser("user").password(encoder.encode("1234")).roles("USER").and()
                .withUser("admin").password(encoder.encode("456")).roles("USER", "ADMIN")
                .and().passwordEncoder(encoder);
    }
}

前面一个bean是密码的加密方式,使用只要选择其中一个就行。

1.3.2连接数据库查找(这里使用的是mybatis和mysql数据库,和springjdbc)

这里的连接操作和mybatis的配置我不在说明了。

配置config配置类:

package com.it.springbootactivit01.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

@Configuration
public class securityconfig {


    @Autowired
    private DataSource dataSource;

    //配置开启记住我功能
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository=new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return jdbcTokenRepository;
    }


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((auth) -> {
                    try {
                        auth
                                .antMatchers("/toLoginHtml").permitAll()
                                .antMatchers("/login").permitAll()
                                //.antMatchers("/test").hasAuthority("admin")
                                //.antMatchers("/test").hasAnyAuthority(new String[]{"zhou","jia"})
                                //.antMatchers("/test").hasRole("jun")
                                //.antMatchers("/test").hasAnyRole(new String[]{"love","liu","yi","fei"})
                                // 所有的请求
                                .anyRequest().authenticated()
                                .and().rememberMe().tokenRepository(persistentTokenRepository())
                                .tokenValiditySeconds(60);

                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }

                }).formLogin()
                // 自定义登录页面地址
                .loginPage("/login.html")
                .loginProcessingUrl("/login")//登录接口自定义,系统自动生成
                .usernameParameter("username")
                .passwordParameter("password")
                .defaultSuccessUrl("/index.html")
                // 放行登录地址(访问登录地址无需已登录状态)
                .permitAll()
                .and().csrf().disable();


        http.exceptionHandling().accessDeniedPage("/unauth.html");
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/logout.html").permitAll();
        // 构建过滤链并返回
        return http.build();
    }



}



解释一下上述代码:

    @Autowired
    private DataSource dataSource;

    //配置开启记住我功能
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository=new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return jdbcTokenRepository;
    }

这里是开启记住我功能,英文要往数据库里写登录的token,使用要告诉security使用的那个数据源。当然开启记住我也要再页面提供一个记住我的按钮和再securityfilterchain开启这个功能。

<input type="checkbox" name="remember-me"/>自动登录
.and().rememberMe().tokenRepository(persistentTokenRepository())
                                .tokenValiditySeconds(60);

当然还有登出操作:

再页面给一个按钮,和再配置类配置相关信息:

<a href="/logout">注销登录</a>
  http.logout().logoutUrl("/logout").logoutSuccessUrl("/logout.html").permitAll();

1.3.3实现数据库密码的查找

package com.it.springbootactivit01.service;

import com.it.springbootactivit01.bean.role;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import com.it.springbootactivit01.mapper.roleMapper;

import javax.annotation.Resource;
import java.util.List;

@Component
public class roleservice implements UserDetailsService {

    @Resource
    roleMapper roleMapper;



    Logger logger = LoggerFactory.getLogger(getClass());


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        logger.info("=====roleservice进入=====");


        logger.info("=====username==="+username);
       /* QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.like("role_name",username);*/
        //role roles=roleMapper.selectOne(queryWrapper);
        role roles=roleMapper.fandone(username);

        List<GrantedAuthority> auths =
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_manager");
        String encode ="{bcrypt}"+new BCryptPasswordEncoder().encode(roles.getRole_state());

        return new User(roles.getRole_name(),encode,auths);
    }


}

好了认证就到这里,下面介绍授权。

1.4授权

1.4.1

授权的操作是再认证的里面给出那些权限,接下来只是看有没有这个权限。

有两种配置方式一个注解一个再配置类里面配置:

 public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((auth) -> {
                    try {
                        auth
                                .antMatchers("/toLoginHtml").permitAll()
                                .antMatchers("/login").permitAll()
                                //.antMatchers("/test").hasAuthority("admin")
                                //.antMatchers("/test").hasAnyAuthority(new String[]{"zhou","jia"})
                                //.antMatchers("/test").hasRole("jun")
                                //.antMatchers("/test").hasAnyRole(new String[]{"love","liu","yi","fei"})
                                // 所有的请求
                                .anyRequest().authenticated()
                                .and().rememberMe().tokenRepository(persistentTokenRepository())
                                .tokenValiditySeconds(60);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }

其中注释掉的地方介绍权限的授权方式。

也可以使用注解的方式,注解的方式要在启动类里加入这个注解来开启这些注解

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

@RequestMapping("/myPage")
    @ResponseBody
    //@Secured({"zhou"})
    //@PreAuthorize("hasAnyAuthority(new String[]{'zhou','li'})")
    //@PostAuthorize("hasAnyRole(new String[]{'zhou','LI'})")
    //@PostFilter(value="filterObject.userid=='1'")
    //@PreFilter(value="filterObject.userid=='1'")
    public void myPage(){
        //测试自定义分页
        Page<role> page=new Page<>(1,3);
        roleMapper.selectPage(page,"1");//调用自定义的查询
        System.out.println("==records=="+page.getRecords());//获取当前页数据 3条记录
        System.out.println(page.getSize());//获取每页的条数 3
        System.out.println(page.getCurrent()); //获取当前页码 1
        System.out.println(page.getPages());//获取总页数 2
        System.out.println(page.getTotal());//获取总记录数 4
        System.out.println(page.hasNext());//获取有没有下一页 true
        System.out.println(page.hasPrevious());//获取是否有上一页 false
    }
}

注释的地方就是。

1.4.2有哪些注解和授权配置

hasAnyAuthority

在4.5.1配置基础上修改代码如下:

.antMatchers("/test").hasAnyAuthority(new String[]{"admin","admin1"})

hasRole

在使用角色的时候需要注意,hasRole方法会在内部紫铜的为角色添加一个前缀:ROLE_,源码如下:

【添加角色判断】

.antMatchers("/test").hasRole("manager")

【在UserService中配置用户角色查询】

List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_manager");

​​​​​​​hasAnyRole

【定义访问/test需要的角色】

.antMatchers("/test").hasAnyRole(new String[]{"manager","sale"})

注解:

​​​​​​​@Secured注解-判断是否有某角色

判断是否具有角色,注意这里匹配的字符串需要添加前缀“ROLE_”

【注解使用方式】

@RequestMapping("/testSecured")
@Secured({"ROLE_manager","ROLE_sale"})
public String testSecured(){
    System.out.println("=============test============");
    return "/show";
}

​​​​​​​@PreAuthorize注解

@PreAuthorize注解会在方法执行前进行权限验证,支持Spring EL表达式,它是基于方法注解的权限解决方案。只有当@EnableGlobalMethodSecurity(prePostEnabled=true)的时候,@PreAuthorize才可以使用

【在启动类中开启注解】

@SpringBootApplication
@MapperScan("com.txc.securitydemo1.mapper")
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SecurityDemo1Application {
    public static void main(String[] args) {
        SpringApplication.run(SecurityDemo1Application.class, args);
    }
}

【具体使用】

//@PreAuthorize("hasAuthority("")")
//@PreAuthorize("hasAnyRole()")
//@PreAuthorize("hasAnyRole(new String[]{'manager','sale'})")
//@PreAuthorize("hasAnyAuthority('manager')")
@RequestMapping("/testPreAuthorize")
public String testPreAuthorize(){
    System.out.println("=============test============");
    return "/show";
}

​​​​​​​@PostAuthorize注解

在方法执行后再进行权限校验,适合验证带有返回值的权限

需要开启prePostEnabled = true才可使用

@PostAuthorize注解的作用:在访问控制器中的相关方法之后(方法的return先不访问),进行权限认证,去看看UserDetailsService用户细节实现类中用户是否有对应的权限,如果有的话,那么控制器方法的最后一句return语句会执行,否则,控制器方法的最后一句return语句不会执行;

【详细使用方式】

//@PostAuthorize("hasAuthority("")")
//@PostAuthorize("hasAnyRole()")
//@PostAuthorize("hasAnyRole(new String[]{'manager','sale'})")
//@PostAuthorize("hasAnyAuthority('manager')")
@PostAuthorize("hasAnyRole('manager1')")
@RequestMapping("/testPostAuthorize")
@ResponseBody
public String testPostAuthorize(){
    System.out.println("=============test============");
    return "/PostAuthorize";
}

​​​​​​​@PostFilter注解

如果控制器方法的return返回值是一个集合,此注解可以对return的这个集合进行过滤输出;

【详细使用方式】

@PostFilter(value="filterObject.userid=='1'")
@RequestMapping("/testPostFilter")
@ResponseBody
public String testPostFilter(){
    System.out.println("=============test============");
    return "/PostAuthorize";
}

​​​​​​​@PreFilter注解

只有在控制器方法的参数是集合类型的时候才能使用@PreFilter注解

【详细使用方式】

 RequestBody注解的主要作用就是用于接收前端的参数,当我们使用post请求的时候,我们会将参数放在request body中,此时我们就需要在Controller的方法的参数前面加上@RequestBody用来接受到前端传过来的request body中的值

@PreFilter(value = "filterObject.username='123'")
@RequestMapping("/testPreFilter")
@ResponseBody
public String testPreFilter(@RequestBody List<Users> list){
    list.forEach(u->{
        System.out.println(u.getUsername());
    });
    return "/PostAuthorize";
}

2springboot整合rabbitmq(默认账号密码都是guest)

​​​​​​​1.1什么是RabbitMQ?

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。

RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。

所有主要的编程语言均有与代理接口通讯的客户端库。

RabbitMQ是一个消息代理:它接受和转发消息。

你可以把它想象成一个邮局:当你把你想要发布的邮件放在邮箱中时,你可以确定邮差先生最终将邮件发送给

你的收件人。在这个比喻中,RabbitMQ是邮政信箱,邮局和邮递员。 
        RabbitMQ和邮局的主要区别在于它不处理纸张,而是接受,存储和转发二进制数据块

1.2安装erlong和rabbitmq

1.2.1安装Erlang

官网下载地址:https://www.erlang.org/downloads

【安装后,配置环境变量】

1.2.2安装RabbitMQ

下载地址:https://www.rabbitmq.com/download.html

​​​​​​​1.2.3激活RabbitMQ's Management Plugin

【激活方式--启动管理界面】

新版可以不配置

命令:rabbitmq-plugins enable rabbitmq_management​​​​​​​

1.2.4安装后启动RabbitMQ命令

net start rabbitmq

​​​​​​​1.2.5通过测试账号测试

用户名:guest  密码:guest

1.3开启rabbit

再配置类里加上开启的注解:

package com.it.springbootrabbitmq;

import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableRabbit
public class SpringbootrabbitmqApplication {

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

}
@EnableRabbit

1.3配置yml文件

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/xianmu?useSSL=false
    driver-class-name: com.mysql.jdbc.Driver

  rabbitmq:
    addresses: localhost #IP??
    username: guest   #???
    password: guest   #??
    dynamic: true     #??????AmqpAdmin?Bean ??:true

1.4创建生产者

 @RequestMapping("/test2")
          @ResponseBody
          public String test2(){
              logger.info("test====初始化成功");

              amqpTemplate.convertAndSend("testDirectExchange","message","{\"code\":\"周加俊\",\"data\":\"测试数据\"}");

              return "hhh";
          }

1.5创建消费者

package com.it.springbootrabbitmq.MabbitMQCustomer;

import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class MabbitMQCustomer {


    @RabbitListener(queuesToDeclare = @Queue("singleQueue"))
    public void process(String message){
        System.out.println("===消费者消费了消息===="+message);
    }


    @RabbitListener(
            bindings = {
                    @QueueBinding(
                            value = @Queue("messageQueue"),
                            exchange = @Exchange(value = "testDirectExchange", type = ExchangeTypes.DIRECT),
                            key = "message")})
    public void process1(String message){
        System.out.println("===消费者消费了消息(direct)===="+message);
    }


}

1.6介绍rabbitmq的四种交换机

1.6.1Direct Exchange交换器

Direct Exchange可以通过routing key属性实现交换机与队列的精准绑定。可以绑定一个也可以绑定多个。绑定的每个队列都能收到相同条数的信息。

如下图所示:队列1与队列2会收到同样条数的信息。

​​​​​​​创建Direct Exchange消费者

1、@RabbitListener:指定消费消息的方法。

2、@Queue("messageQueue")项目启动的时候创建名称为messageQueue队列

3、@Exchange(value="testDirectExchange",type= ExchangeTypes.DIRECT):

   项目启动的时候,创建名称为testDirectExchange交换机,类型是direct

4、key = "message"):使用message将交换机testDirectExchangemessageQueue队列绑定

   以此类推我们可以实现通过mail将交换机testDirectExchangemailQueu队列绑定

@Component
public class MabbitMQCustomer {

    @RabbitListener(
        bindings = {
           @QueueBinding(
               value = @Queue("messageQueue"),
               exchange = @Exchange(value="testDirectExchange",type= ExchangeTypes.DIRECT),
               key = "message")})
    public void process1(String message){
        System.out.println("===消费者消费了消息(direct)===="+message);
    }

}

​​​​​​​创建Direct Exchange生产者

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMQProducer {
        @Autowired
        private AmqpTemplate amqpTemplate;
        @Test
        public void send(){      
amqpTemplate.convertAndSend("testDirectExchange",
"message","{\"code\":\"200\",\"data\":\"测试数据\"}");
        }
}

1.6.2Topic Exchange交换机

Direct Exchange是精准匹配,而Topic Exchange是模糊匹配。

routing key 可以是类似 *.orange.* 或者 lazy.# 的表达式。其中,* (星) 代表一个单词,# (hash) 代表0个或多个单词

创建Topic Exchange消费者

@RabbitListener(
    bindings = {
      @QueueBinding(
        value = @Queue("topic.Queue1"),
        exchange = @Exchange(value="testTopicExchange",
        type= ExchangeTypes.TOPIC),
        key = "topic.*")})
public void process2(String message){
    System.out.println("===消费者消费了消息(topic)===="+message);
}

​​​​​​​​​​​​​​创建Topic Exchange生产者

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMQProducer {
        @Autowired
        private AmqpTemplate amqpTemplate;
        @Test
        public void send(){
            amqpTemplate.convertAndSend("testTopicExchange","topic.queue1",
"{\"code\":\"200\",\"data\":\"测试topic\"}");
        }
}

1.6.3Fanout Exchange交换机

和direct exchange、topic exchange不同,fanout exchange不使用routing key,它会将消息路由到所有与其绑定的队列。

fanout exchange是消息广播路由的理想选择。

和direct exchange的一个交换机绑定多个队列的情况一样,绑定了fanout exchange的队列,都会接收到一份全量的消息。

创建Fanout Exchange消费者

@RabbitListener(
    bindings = {
      @QueueBinding(
         value = @Queue("fanout.Queue1"),
         exchange = @Exchange(value="testFanoutExchange",
           type= ExchangeTypes.FANOUT),
         key = "fanout.*")})
public void process3(String message){
    System.out.println("===消费者消费了消息(fanout)===="+message);
}

创建Fanout Exchange生产者

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMQProducer {
        @Autowired
        private AmqpTemplate amqpTemplate;

        @Test
        public void send(){
            amqpTemplate.convertAndSend("testFanoutExchange","fanout.queue1",
"{\"code\":\"200\",\"data\":\"测试fanout\"}");
        }
}

1.6.4Headers Exchange交换机

和上面三种交换机类型不同,headers exchange是根据Message的一些头部信息来分发过滤Message的,它会忽略routing key的属性,如果Header信息和message消息的头信息相匹配,那么这条消息就匹配上了。

有一个重要参数x-match:当“x-match”参数设置为“any”时,只要一个匹配的header 属性值就足够了;当“x-match”设置为“all”时,意味着所有值都必须匹配,才能将交换机和队列绑定上。

​​​​​​​创建Headers Exchange配置类

用于实例化Queue, HeadersExchange以及队列和交换机的绑定关系

@Configuration
public class HeadersConfig {

    @Bean
    public Queue createQueue(){
        return new Queue("headersQueue1");
    }

    @Bean
    public HeadersExchange headersExchange(){
        return new HeadersExchange("headerExchange");
    }


    @Bean
    public Binding binding1(){
        Map<String,Object> header=new HashMap<>();
        header.put("type","1");
        header.put("name","send1");
        return BindingBuilder
                .bind(new Queue("headersQueue1"))
                .to(headersExchange())
                .whereAny(header)
                //.whereAll(header)
                .match();
    }
}

​​​​​​​创建Headers Exchange消费者

@Component
public class MabbitMQCustomer {
    @RabbitListener(queues ="headersQueue1")
    public void process4(String message){
        System.out.println("===消费者消费了消息(headers)===="+message);
    }
}

创建Headers Exchange生产者

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMQProducer {
        @Autowired
        private AmqpTemplate amqpTemplate;

        @Test
        public void send(){
            MessageProperties messageProperties=new MessageProperties();
            messageProperties.setHeader("type","1");
            messageProperties.setHeader("name","send1");
            Message message=new Message("这是消息内容".getBytes(StandardCharsets.UTF_8)
,messageProperties);
            amqpTemplate.convertAndSend("headerExchange","",message);
        }
}

好了几天的介绍就到这里了!!!!!!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值