<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于redis的分布式認(rèn)證鑒權(quán)解決方案

          共 16622字,需瀏覽 34分鐘

           ·

          2021-04-17 19:14

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          項(xiàng)目介紹

          方案介紹

          基于數(shù)據(jù)庫的認(rèn)證鑒權(quán)方式,登錄之后會(huì)產(chǎn)生一個(gè)session存儲(chǔ)在內(nèi)存之中,然后將sessionId返回給客戶端,后續(xù)客戶端請求資源時(shí),會(huì)將sessionId攜帶上,服務(wù)端根據(jù)sessionId判斷用戶的登錄狀態(tài),如果用戶登錄過,則放行,如果沒有登錄,則會(huì)被攔截,跳轉(zhuǎn)到登錄頁面重新登錄,但這種將登錄狀態(tài)以及信息放在內(nèi)存中的方式會(huì)帶來兩個(gè)問題:

          • 由于session產(chǎn)生后放在服務(wù)器的內(nèi)存之中,服務(wù)器因?yàn)槟撤N原因(宕機(jī)或者更新)重啟之后,則所有的session都會(huì)丟失,那么登錄過的所有用戶都需要重新登錄;

          • 分布式場景下,可能登錄是在服務(wù)器A上,那么session保存在服務(wù)器A上,下一個(gè)請求,可能由服務(wù)器B處理,那么服務(wù)器B上沒有這個(gè)session,就需要重新登錄;

          通過將session保存到redis中實(shí)現(xiàn)session共享解決分布式場景下的以上兩個(gè)問題,

          項(xiàng)目結(jié)構(gòu)

          項(xiàng)目源碼:https://github.com/xdouya/Spring-Security-demo/tree/master/04-mybatis-redis-security


          項(xiàng)目構(gòu)建

          數(shù)據(jù)表創(chuàng)建以及用戶數(shù)據(jù)導(dǎo)入

          DROP TABLE IF EXISTS `users`;
          CREATE TABLE `users` (
            `username` varchar(50) NOT NULL,
            `password` varchar(500) NOT NULL,
            `enabled` tinyint(1) NOT NULL,
            PRIMARY KEY (`username`)
          ) ENGINE=InnoDB;

          INSERT  IGNORE INTO `users` VALUES ('admin','{bcrypt}$2a$10$SAqQq0WEQYRA4etZpRa6e.Kew0sKKtC/ahFrSZXS1iHsy5EhZqLsa',1),('user','{bcrypt}$2a$10$SAqQq0WEQYRA4etZpRa6e.Kew0sKKtC/ahFrSZXS1iHsy5EhZqLsa',1),('vip','{bcrypt}$2a$10$SAqQq0WEQYRA4etZpRa6e.Kew0sKKtC/ahFrSZXS1iHsy5EhZqLsa',1);

          DROP TABLE IF EXISTS `authorities`;
          CREATE TABLE `authorities` (
            `username` varchar(50) NOT NULL,
            `authority` varchar(50) NOT NULL,
            UNIQUE KEY `ix_auth_username` (`username`,`authority`),
            CONSTRAINT `fk_authorities_users` FOREIGN KEY (`username`) REFERENCES `users` (`username`)
          ) ENGINE=InnoDB;

          INSERT  IGNORE INTO `authorities` VALUES ('admin','ROLE_admin'),('user','ROLE_user'),('vip','ROLE_vip');


          這里的這個(gè)users表和authorities表的字段沒有強(qiáng)制要求,只要后續(xù)查詢的時(shí)候能對應(yīng)上就可以了;


          Maven依賴

          • pom.xml

          <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-data-redis</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.session</groupId>
                  <artifactId>spring-session-data-redis</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-security</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.mybatis.spring.boot</groupId>
                  <artifactId>mybatis-spring-boot-starter</artifactId>
                  <version>2.1.3</version>
              </dependency>
              <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>druid-spring-boot-starter</artifactId>
                  <version>1.1.10</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-devtools</artifactId>
                  <scope>runtime</scope>
                  <optional>true</optional>
              </dependency>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <scope>runtime</scope>
              </dependency>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <optional>true</optional>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
                  <exclusions>
                      <exclusion>
                          <groupId>org.junit.vintage</groupId>
                          <artifactId>junit-vintage-engine</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
              <dependency>
                  <groupId>io.projectreactor</groupId>
                  <artifactId>reactor-test</artifactId>
                  <scope>test</scope>
              </dependency>
              <dependency>
                  <groupId>org.springframework.security</groupId>
                  <artifactId>spring-security-test</artifactId>
                  <scope>test</scope>
              </dependency>
          </dependencies>

          Spring Security配置

          • Srping Security配置

          @EnableWebSecurity
          public class SecurityConfig extends WebSecurityConfigurerAdapter {

              /**
               * 配置攔截器保護(hù)請求
               */
              @Override
              protected void configure(HttpSecurity http) throws Exception {
                  http.authorizeRequests()
                          .antMatchers("/admin/**").hasRole("admin")
                          .antMatchers("/vip/**").hasRole("vip")
                          .antMatchers("/user/**").hasRole("user")
                          .anyRequest().authenticated()
                          .and().formLogin()
                          .and().httpBasic();
              }

              /**
               * 根據(jù)自動(dòng)匹配密碼編碼器
               * @return PasswordEncoder
               */
              @Bean
              public PasswordEncoder passwordEncoder(){
                  return PasswordEncoderFactories.createDelegatingPasswordEncoder();
              }
          }

          • Redis配置

          /**
           * @author caiwl
           * @date 2020/8/21 16:58
           */
          @Configuration
          public class RedisConfig {
              @Bean
              public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
                  RedisTemplate<String, Object> template = new RedisTemplate<>();
                  template.setConnectionFactory(redisConnectionFactory);
                  GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
                  template.setKeySerializer(new StringRedisSerializer());
                  template.setValueSerializer(genericJackson2JsonRedisSerializer);
                  template.setHashKeySerializer(new StringRedisSerializer());
                  template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
                  template.afterPropertiesSet();
                  return template;
              }
          }

          • spring-session配置

          @Configurable
          @EnableRedisHttpSession
          public class SessionConfig {
          }

          • MyUserDetailServiceImpl,通過實(shí)現(xiàn)UserDetailsService#loadUserByUsername方法自定用戶信息查詢

          /**
           * 自定義UserDetailsService
           * @author caiwl
           * @date 2020/8/20 17:06
           */
          @Service
          public class MyUserDetailServiceImpl implements UserDetailsService {

              private UserDao userDao;

              @Autowired
              public MyUserDetailServiceImpl(UserDao userDao){
                  this.userDao = userDao;
              }

              @Override
              public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                  System.out.println("整合mybatis 查詢用戶信息");
                  return userDao.loadUserByUsername(username);
              }
          }

          • UserDao,用戶數(shù)據(jù)訪問接口

          /**
           * @author caiwl
           * @date 2020/8/20 17:09
           */
          public interface UserDao {
              /**
               * 根據(jù)用戶名查詢用戶信息
               * @param username 用戶名
               * @return 用戶信息
               */
              UserPo loadUserByUsername(String username);
          }

          • UserMapper.xml, mapper文件

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

          <mapper namespace="org.dy.security.dao.UserDao">
           <resultMap type="org.dy.security.entiy.UserPo" id="UserMap">
            <id column="username" property="username"/>
            <result column="password" property="password"/> 
            <collection property="authorities" ofType="org.dy.security.entiy.RolePo">
             <id column="username" property="username"/>
             <result column="authority" property="authority"/>
            </collection>  
           </resultMap>

           <select id="loadUserByUsername" resultMap="UserMap">
            select 
             users.username, users.password, authorities.authority
            from 
             users left join authorities on users.username = authorities.username
             where users.username=#{username}
           </select>
          </mapper>

          • UserPo,用戶信息

          /**
           * @author caiwl
           * @date 2020/8/20 17:08
           */
          @Data
          public class UserPo implements UserDetails, Serializable {

              private static final long serialVersionUID = 1L;

              private String username;
              private String password;
              private List<RolePo> authorities;

              @Override
              public Collection<? extends GrantedAuthority> getAuthorities() {
                  return authorities;
              }

              @Override
              public String getPassword() {
                  return password;
              }

              @Override
              public String getUsername() {
                  return username;
              }

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

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

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

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

          • RolePo,角色信息

          /**
           * @author caiwl
           * @date 2020/8/20 17:09
           */
          @Data
          public class RolePo implements GrantedAuthority {

              private static final long serialVersionUID = 1L;
              private String username;
              private String authority;

              @Override
              public String getAuthority() {
                  return authority;
              }
          }

          • HelloController,控制器接口

          /**
           * @author caiwl
           * @date 2020/8/9 14:05
           */
          @RestController
          public class HelloController {

              @GetMapping("/")
              public String hello(){
                  return "hello, welcome";
              }

              @GetMapping("/admin/hello")
              public String helloAdmin(){
                  return "hello admin";
              }

              @GetMapping("/vip/hello")
              public String helloVip(){
                  return "hello vip";
              }

              @GetMapping("/user/hello")
              public String helloUser(){
                  return "hello user";
              }
          }

          項(xiàng)目測試

          訪問http://localhost:8080/,輸入用戶名,密碼admin:088114訪問,可以發(fā)現(xiàn)session保存在redis里面了;


          思考

          以上介紹的都是基于session來記錄用戶的登錄狀態(tài),這種基于session的方式有如下幾個(gè)問題

          • 每一個(gè)用戶都會(huì)生成一個(gè)session,當(dāng)用戶量巨大的時(shí)候,巨量的session會(huì)占用巨大的服務(wù)端內(nèi)存

          • 可以通過將session緩存在redis中共享,解決分布式場景下的登錄狀態(tài)記錄,但是一旦redis宕機(jī)后,會(huì)造成所有用戶都無法訪問任何需要認(rèn)證的資源;

          ————————————————

          版權(quán)聲明:本文為CSDN博主「dy豆芽」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

          原文鏈接:

          https://blog.csdn.net/douya2016/article/details/108222273






          鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布

          ??????

          ??長按上方微信二維碼 2 秒





          感謝點(diǎn)贊支持下哈 

          瀏覽 145
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄 色 视 频在线观看 久久国产精品波多野结衣AV | 亚洲社区电影 | 国产免费网址 | 影音先锋女人av资源站 | 中文字幕+乱码+中文字幕17c |