国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

這篇具有很好參考價(jià)值的文章主要介紹了微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

??可能有些人會(huì)覺得這篇似曾相識(shí),沒錯(cuò),這篇是由原文章進(jìn)行二次開發(fā)的。

前陣子有些事情,但最近看到評(píng)論區(qū)說原文章最后實(shí)現(xiàn)的是單模塊的驗(yàn)證,由于過去太久也懶得驗(yàn)證,所以重新寫了一個(gè)完整的可以跑得動(dòng)的一個(gè)。

OK,回到正題,以下是真正對(duì)應(yīng)的微服務(wù)多模塊的一個(gè)方法,使用到的技術(shù)有:基于微服務(wù)的Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT。

對(duì)使用到的微服務(wù)技術(shù)進(jìn)行在項(xiàng)目中的說明:

Security:負(fù)責(zé)登錄驗(yàn)證(文章中沒有實(shí)現(xiàn)授權(quán),在過濾器中直接返回null,如果想實(shí)現(xiàn)授權(quán),可以在返回null的地方添加授權(quán)信息類似ROLE_ADMIN,同時(shí)在Security的配置文件那里添加授權(quán)信息即可)。

Redis:負(fù)責(zé)緩存token跟用戶數(shù)據(jù)。

Gateway:對(duì)前端提供的接口,由它多個(gè)模塊進(jìn)行接口調(diào)用。

OpenFeign:提供給security查詢數(shù)據(jù)庫(kù)中的用戶信息。

Nacos:注冊(cè)服務(wù)中心,注冊(cè)服務(wù)的信息,使OpenFeign可以調(diào)用其他服務(wù)模塊。

注意:雖然是原文章的二次編寫,但是很多都不同,建議直接跟著這篇走。

目錄

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

2.Common模塊

pom.xml

2.1 RedisConfig

2.2?RedisUtil

2.3 ResponseUtil

2.4??TokenUtil

?????????2.5??CorConfig

3.model模塊

3.1 pom

3.2 User

?????????3.3 UserFeign

4.service模塊

? 4.1 目錄結(jié)構(gòu)?編輯

? 4.2 service_user模塊

? ? 4.2.1 pom.xml

? ? 4.2.2?application.yml

? ? 4.2.3 Service_UserApp啟動(dòng)類

4.3?其他service模塊

5.spring_security模塊

5.1 pom

5.2?DiyUserDetails(UserDetails)

5.3?WebSecurityConfig(WebSecurityConfigurerAdapter)

5.4?TokenOncePerRequestFilter(OncePerRequestFilter)

5.5?LoginAuthenticationEntryPoint(AuthenticationEntryPoint)

5.6?LoginInFailHandler(AuthenticationFailureHandler)

5.7?LoginInSuccessHandler(AuthenticationSuccessHandler)

5.8?LogOutSuccessHandler(LogoutSuccessHandler)

5.9?NothingAccessDeniedHandler(AccessDeniedHandler)

?????????5.10?MyUserDetailService(UserDetailsService)

6.gateway模塊

6.1 pom

6.2 application.yml

7.測(cè)試


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

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

涉及的模塊有

(1)common(Redis配置文件、Redis工具、Token工具、返回給前端信息的工具;即如下文件RedisConfig、RedisUtil、TokenUtil、ResponseUtil);

(2)gateway

(3)model(實(shí)體類,F(xiàn)eign的客戶端);

(4)service(用戶模塊、課程模塊);

(5)spring_security(security的過濾器跟配置文件)。

下面小編將全部一一介紹并且源碼展示出來。

2.Common模塊

pom.xml

        <!--springboot_redis緩存框架 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

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

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <dependency>
            <groupId>com.goyes</groupId>
            <artifactId>model</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

2.1 RedisConfig

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
/*
 * Redis配置
 * 解決redis在業(yè)務(wù)邏輯處理層上不出錯(cuò),緩存序列化問題
 * */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
    @Resource
    RedisConnectionFactory redisConnectionFactory;
    @Bean
    public RedisTemplate<String,Object> redisTemplate(){
        RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
//Json序列化配置
        //1、String的序列化
        StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);

        //2、json解析任意的對(duì)象(Object),變成json序列化
        Jackson2JsonRedisSerializer<Object> serializer=new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper mapper=new ObjectMapper(); //用ObjectMapper進(jìn)行轉(zhuǎn)義
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //該方法是指定序列化輸入的類型,就是將數(shù)據(jù)庫(kù)里的數(shù)據(jù)按照一定類型存儲(chǔ)到redis緩存中。
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);

        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(serializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(serializer);

        return redisTemplate;
    }
}

2.2?RedisUtil

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    public static StringRedisTemplate stringRedisTemplateStatic;
    @PostConstruct //在項(xiàng)目啟動(dòng)的時(shí)候執(zhí)行該方法,也可以理解為在spring容器初始化的時(shí)候執(zhí)行該方法。
    public void initStringRedisTemplate(){
        stringRedisTemplateStatic=this.stringRedisTemplate;
    }

    private static final DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /*
     * 保存token信息到redis,也可直接在創(chuàng)建token中使用該方法
     * */
    public static  void redis_SaveTokenInfo(String token,String username){
        //以u(píng)sername做key
        LocalDateTime localDateTime=LocalDateTime.now();
        stringRedisTemplateStatic.opsForHash().put(username,"token",token);
        stringRedisTemplateStatic.opsForHash().put(username,"refreshTime", //有效時(shí)間
                df.format(localDateTime.plus(7*24*60*60*1000, ChronoUnit.MILLIS)));
        stringRedisTemplateStatic.opsForHash().put(username,"expiration",  //過期時(shí)間 5分鐘 300秒
                df.format(localDateTime.plus(300*1000, ChronoUnit.MILLIS)));
        stringRedisTemplateStatic.expire(username,7*24*60*60*1000, TimeUnit.SECONDS);
    }

    /*
     * 檢查redis是否存在token
     * */
    public static boolean hasToken(String username){
        return stringRedisTemplateStatic.opsForHash().getOperations().hasKey(username);
    }
}

2.3 ResponseUtil

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import org.apache.ibatis.annotations.Result;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@Data
public class ResponseUtil {
    public static int OK = 200;
    public static int ERROR = 404;
    public static String SUCCESS="操作成功!";
    public static String NO_SUCCESS="操作失敗,請(qǐng)稍候重試。";
    //返回碼(200)
    private int code;
    //返回消息
    private String message;

    @ApiModelProperty(value = "返回?cái)?shù)據(jù)(單條或多條)")
    private Map<Object, Object> data = new HashMap<Object, Object>();


    public ResponseUtil(int code, String message) {
        this.code=code;
        this.message=message;
    }
    public ResponseUtil(int code, String message, Map<Object, Object> data) {
        this.code=code;
        this.message=message;
        this.data=data;
    }
    //對(duì)response寫入Object數(shù)據(jù)
    public static void reponseOutDiy(HttpServletResponse response,int statusCode , Object result) {
        ObjectMapper mapper = new ObjectMapper();
        PrintWriter writer = null;
        response.setStatus(statusCode);
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        try {
            writer = response.getWriter();
            mapper.writeValue(writer, result);
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                writer.flush();
                writer.close();
            }
        }
    }

}

2.4??TokenUtil

import com.Lino_white.model.User; //model模塊的user
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class TokenUtil {
    public static final String APP_SECRET ="Lino_white"; //隨便取,你的Token密鑰
    public static final String TOKEN_HEAD="Authorization";

    public static final String TOKEN_PREFIX = "Bearer ";
    public static String createToken(User user){
        String token = Jwts.builder()
                .setId(String.valueOf(user.getId()))
                .setSubject(user.getUsername())
                .setIssuedAt(new Date()) //簽發(fā)時(shí)間
                .setIssuer("Lino_white") //簽發(fā)者
                .setExpiration(new Date(System.currentTimeMillis() + 300* 1000)) //過期時(shí)間 5分鐘 自行設(shè)置
                .signWith(SignatureAlgorithm.HS256, APP_SECRET) //簽名算法跟密鑰
                .claim("identity", user.getIdentity()) //可添加額外的屬性
                .compact();
        return token;
    }

//重新生成新的Token,異常時(shí)間由傳入的參數(shù)決定
    public static String createToken(User user,Date expirationTime){
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            expirationTime= (Date) f.parse(f.format(expirationTime));
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
       
        String token = Jwts.builder()
                .setId(String.valueOf(user.getId()))
                .setSubject(user.getUsername())
                .setIssuedAt(new Date()) //簽發(fā)時(shí)間
                .setIssuer("Lino_white") //簽發(fā)者
                .setExpiration(expirationTime) //過期時(shí)間
                .signWith(SignatureAlgorithm.HS256, APP_SECRET) //簽名算法跟密鑰
                .claim("identity", user.getIdentity()) //可添加額外的屬性
                .compact();
        return token;
    }

    //獲得用戶名
    public String getUsernameFromToken(String token){
        return Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(token).getBody().getSubject();
    }
    /**
     * 判斷token是否存在與有效(1)
     */
    public boolean checkToken(String token){
        if (StringUtils.isEmpty(token)) return false;
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(token);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 判斷token是否存在與有效(2)
     */
    public boolean checkToken(HttpServletRequest request){
        try {
            String token = request.getHeader("token");
            return checkToken(token);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    //獲得全部屬性
    public Claims parseJwt(String token){
        Claims claims = Jwts.parser()
                .setSigningKey(APP_SECRET) // 設(shè)置標(biāo)識(shí)名
                .parseClaimsJws(token)  //解析token
                .getBody();
        return claims;
    }
    //獲得指定屬性
    public String getTokenClaim(String token,String key){
        Claims body = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(token).getBody();
        return String.valueOf(body.get(key));
    }
}

2.5??CorConfig

package com.goyes.common.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 解決跨域
 * @author: white
 */
@Configuration
public class CorConfig implements WebMvcConfigurer {


    @Override
    public void addCorsMappings(CorsRegistry registry) {
        System.out.println("開始解決跨域");
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
//                .allowCredentials(true)//是否有驗(yàn)證,有就打開
                .allowedHeaders("*")
                .maxAge(3600);
    }

}

3.model模塊

3.1 pom

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>

3.2 User

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel(value = "實(shí)體:用戶")
@TableName("user")
public class User implements Serializable {
    @ApiModelProperty("用戶id")
    @TableId(value = "id",type = IdType.AUTO)
    private int id;
    @ApiModelProperty("用戶名")
    private String username;
    @ApiModelProperty("密碼")
    private String password;
    @TableField("identity")
    @ApiModelProperty("身份")
    private String identity;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", identity='" + identity + '\'' +
                '}';
    }
}

3.3 UserFeign

package com.goyes.model.client;

import com.goyes.model.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "service-user")
public interface UserFeign {
    @GetMapping("/api/user/{username}")
    public User findUserByName(@PathVariable("username") String username);

}

4.service模塊

? 4.1 目錄結(jié)構(gòu)

? 4.2 service_user模塊

? ? 4.2.1 pom.xml

注意:service_user接入了security模塊。

        <!--openfeign 遠(yuǎn)程接口調(diào)用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--nacos 注冊(cè)中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--nacos 配置中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <!--nacos 客戶端-->
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

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

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>com.goyes</groupId>
            <artifactId>model</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>com.goyes</groupId>
            <artifactId>common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

        <!--加入各service模塊,swagger文檔實(shí)現(xiàn)接入-->
        <dependency>
            <groupId>com.goyes</groupId>
            <artifactId>service_other</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.goyes</groupId>
            <artifactId>service_course</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.goyes.service_comment</groupId>
            <artifactId>service_comment</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

        <!--接入security模塊-->
        <dependency>
            <groupId>com.goyes</groupId>
            <artifactId>spring_security</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

? ? 4.2.2?application.yml

server:
  port: 8001
spring:
  application:
    name: service-user
  main:
    allow-bean-definition-overriding: true
  profiles:
    active: dev
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        group: dev
      discovery:
        cluster-name: WHITE
feign:
  client:
    config:
      default:
        connect-timeout: 10000
        read-timeout: 10000

? ? 4.2.3 Service_UserApp啟動(dòng)類

@SpringBootApplication
@EnableSwagger2WebMvc
@EnableDiscoveryClient
@EnableFeignClients
@EnableCaching
public class Service_UserApp
{
    public static void main( String[] args )
    {
        SpringApplication.run(Service_UserApp.class, args);
    }
}

? ? 4.2.4 ApiController控制器

在任意一個(gè)控制器中,添加如下代碼,該接口將用于OpenFeign的遠(yuǎn)程接口調(diào)用,由security模塊中的自定義類MyUserDetailService去進(jìn)行調(diào)用。(MyUserDetailService的代碼在介紹security模塊中會(huì)出現(xiàn))

    @GetMapping("/api/user/{username}")
    public User findUserByName(@PathVariable("username") String username){
        User userByName = userService.findUserByName(username);
        return userByName;
    }

4.3?其他service模塊

對(duì)于其他模塊,相對(duì)應(yīng)跟service_user模塊一樣,進(jìn)行如下操作即可:

(1)在pom.xml中引入security模塊

        <!--接入security模塊-->
        <dependency>
            <groupId>com.goyes</groupId>
            <artifactId>spring_security</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

(2)在application.xml中添加以下代碼

spring:
  main:
    allow-bean-definition-overriding: true

????????防止出現(xiàn)運(yùn)行異常報(bào)錯(cuò)信息,對(duì)于同一個(gè)服務(wù)的FeignClient來說,配置該屬性不會(huì)造成覆蓋,詳情可以查看該文章:Consider renaming one of the beans:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

5.spring_security模塊

5.1 pom

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--security安全框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--springboot_redis緩存框架 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <dependency>
            <groupId>com.goyes</groupId>
            <artifactId>model</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.goyes</groupId>
            <artifactId>common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

5.2?DiyUserDetails(UserDetails)

import com.Lino_white.model.User;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
@Data
@EqualsAndHashCode(callSuper = false)
public class DiyUserDetails extends User implements UserDetails, Serializable {

    //用戶權(quán)限列表
    private Collection<String> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities1 = new ArrayList<>();
        for(String permissionValue : authorities) {
            if(StringUtils.isEmpty(permissionValue)) continue;
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
            authorities1.add(authority);
        }

        return authorities1;
    }

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

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

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

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

5.3?WebSecurityConfig(WebSecurityConfigurerAdapter)

注意:前面將對(duì)service_user的遠(yuǎn)程接口定義為/api/user/{username},所以在過濾方面要放行該路徑,否則security無法調(diào)用數(shù)據(jù)庫(kù)查詢用戶信息,導(dǎo)致程序報(bào)錯(cuò)。

對(duì)此,可以查看該文章feign.FeignException$Unauthorized

package com.goyes.spring_security.config;


import com.goyes.spring_security.filter.TokenAuthenticationFilter;
import com.goyes.spring_security.filter.TokenLoginFilter;
import com.goyes.spring_security.filter.TokenOncePerRequestFilter;
import com.goyes.spring_security.handler.*;
import com.goyes.spring_security.service.MyUserDetailService;
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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsUtils;

@Configuration
@EnableWebSecurity //開啟Security功能
@EnableGlobalMethodSecurity(prePostEnabled = true) //啟動(dòng)方法級(jí)別的權(quán)限認(rèn)證
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailService myUserDetailService;

    @Bean
    //配置密碼加密器
    public PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}

    //配置哪些請(qǐng)求不攔截
    //TODO 將需要Feign的方法前綴都用上api,得到api/select/user/{user_id}這樣的路徑不受限制
    // 由于api路徑是由服務(wù)模塊自己去調(diào)用的,所以gateway不用做路徑請(qǐng)求的處理
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/api/**","/doc.html#/**","/swagger-resources");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailService).passwordEncoder(passwordEncoder());
    }

    //配置安全策略
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("讀取配置*****************WHITE");
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                //該過濾器設(shè)置在用戶名、密碼、權(quán)限過濾器之前。這樣每次訪問接口都會(huì)經(jīng)過此過濾器,我們可以獲取請(qǐng)求路徑,并判定當(dāng)請(qǐng)求路徑為/login時(shí)進(jìn)入驗(yàn)證碼驗(yàn)證流程。
                // 使用jwt的Authentication,來解析過來的請(qǐng)求是否有token
                .addFilterBefore(new TokenOncePerRequestFilter(), UsernamePasswordAuthenticationFilter.class)

                //登錄后,訪問沒有權(quán)限處理類
                .exceptionHandling().accessDeniedHandler(new NothingAccessDeniedHandler())
                //匿名訪問,沒有權(quán)限的處理類
                .authenticationEntryPoint(new LoginAuthenticationEntryPoint())

                .and()
                .formLogin()
                .successHandler(new LoginInSuccessHandler())
                .failureHandler(new LoginInFailHandler())
                .and()
                .logout()
                .logoutSuccessHandler(new LogOutSuccessHandler())
                // 配置取消session管理
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .csrf().disable();
    }
}

5.4?TokenOncePerRequestFilter(OncePerRequestFilter)

注意:在這里TokenUtil跟RedisUtil對(duì)于過期時(shí)間的定義不同。

token過期時(shí)間為3分鐘,redis上存儲(chǔ)的異常時(shí)間為5分鐘,并且redis上存儲(chǔ)的刷新時(shí)間為7天

在下面的配置文件中,僅僅對(duì)token進(jìn)行分析而已,可以根據(jù)需要在這里做驗(yàn)證碼校驗(yàn)。

token的過期時(shí)間在以下代碼中是這樣做的,當(dāng)token過期時(shí)間3分鐘到了,判斷redis上存儲(chǔ)的異常時(shí)間是否到了5分鐘,沒到5分鐘就返回一個(gè)新的token給前端,前端拿到該token就可以繼續(xù)訪問;如果到了5分鐘,則會(huì)停止訪問并通知前端 “用戶已經(jīng)過期,請(qǐng)重新登錄”。

小編有個(gè)想法(還沒做):在這里可以重新定義過期時(shí)間,比如用戶每次訪問時(shí)都進(jìn)行判斷:當(dāng)token的過期時(shí)間小于1分鐘后就刷新redis的異常時(shí)間,這樣可以使當(dāng)token要過期時(shí),就有新的token出現(xiàn),但這樣操作也存在缺點(diǎn):就是要消耗內(nèi)存資源,每次都得去讀取token是否臨近過期時(shí)間了。對(duì)于這塊,可以針對(duì)自己的情況去做調(diào)整。

package com.goyes.spring_security.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.goyes.common.utils.RedisUtil;
import com.goyes.common.utils.ResponseUtil;
import com.goyes.common.utils.TokenUtil;
import com.goyes.model.User;
import com.goyes.spring_security.model.DiyUserDetails;
import io.github.classgraph.json.JSONUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jdk.nashorn.internal.parser.JSONParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.json.JsonParser;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import springfox.documentation.spring.web.json.Json;
import sun.security.util.SecurityConstants;
//import sun.security.util.SecurityConstants;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;

/**
 * 在用戶名、密碼、權(quán)限過濾器之前的過濾器
 * 在請(qǐng)求過來的時(shí)候,解析請(qǐng)求頭中的token,再解析token得到用戶信息,再存到SecurityContextHolder中
 *
 * TODO 下面過濾器僅做了針對(duì)token解析,包括token異常、過期、重新頒布等
 * @author white
 */
@Component
public class TokenOncePerRequestFilter extends OncePerRequestFilter {

    @Autowired
    StringRedisTemplate stringRedisTemplate = RedisUtil.stringRedisTemplateStatic;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {

        /*
        * TODO 可在這里判斷請(qǐng)求過來的路徑是否為login,方式為post,來在這里進(jìn)行驗(yàn)證碼有效驗(yàn)證
        *  驗(yàn)證成功則直接chain(request,response)繼續(xù)走過濾
        * */
        String requestURI = request.getRequestURI();
        System.out.println("開始請(qǐng)求,請(qǐng)求路徑:"+requestURI+"  請(qǐng)求方式:"+request.getMethod());
        User user = null;
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String authHeader = request.getHeader(TokenUtil.TOKEN_HEAD);

        //沒有token不用理
        if (authHeader != null && authHeader.startsWith(TokenUtil.TOKEN_PREFIX)) {

            final String authToken = authHeader.replace(TokenUtil.TOKEN_PREFIX, "");
            //這里的authToken可能時(shí)間已過,需要重新創(chuàng)建一個(gè)token
            //先對(duì)比redis中的過期時(shí)間,redis的過期時(shí)間隨著用戶的操作而更新,token可能沒有及時(shí)更新
            //判斷是否一樣,一樣的話就是token失效了,跳轉(zhuǎn)重新登錄,
            // 不一樣就是redis過期時(shí)間更新了,生成新的token返回給前端
            String username = null;
            Claims claims;
            try {
                claims = new TokenUtil().parseJwt(authToken);
                username = claims.getSubject();
            } catch (ExpiredJwtException e) {
                //token過期
                claims = e.getClaims();
                username = claims.getSubject();
                user = JSONObject.parseObject(String.valueOf(stringRedisTemplate.opsForHash().get(username, "user")), User.class);

                if (user == null) {
                    chain.doFilter(request, response);
                    return;
                } else {
                    if (RedisUtil.hasToken(username)) {
                        Object expiration = stringRedisTemplate.opsForHash().get(username, "expiration");
                        Object tokenExpirationTime = f.format(claims.getExpiration());
                        Date expirationDate_redisTime = null, expirationDate_tokenTime = null, nowTime;
                        try {
                            expirationDate_redisTime = (Date) f.parseObject(String.valueOf(expiration));
                            expirationDate_tokenTime = (Date) f.parseObject(String.valueOf(tokenExpirationTime));
                            nowTime = (Date) f.parseObject(f.format(new Date()));
                        } catch (ParseException ex) {
                            throw new RuntimeException(ex);
                        }

                        System.out.println("*********Token過期(Start)***********");
                        System.out.println("token瀏覽器過期時(shí)間:" + tokenExpirationTime);

                        System.out.println("redis過期時(shí)間:" + expiration);

                        //
                        /*
                         * redis<token || token=redis || redis <now  則token失效,跳轉(zhuǎn)登錄
                         * token<redis
                         * */
                        if (expirationDate_redisTime.getTime() < expirationDate_tokenTime.getTime() ||
                                expirationDate_tokenTime.getTime() == expirationDate_redisTime.getTime() ||
                                expirationDate_redisTime.getTime() < nowTime.getTime()) {

                            //時(shí)間相同,跳轉(zhuǎn)登錄
                            ResponseUtil.reponseOutDiy(response, 401, "用戶已過期,請(qǐng)重新登錄");
                            System.out.println("*********Token過期(End)失效***********");
                            return;
                        } else {
                            //時(shí)間不同,生成新token  需要用戶id,身份,用戶名
                            //response存入token   返回
                            Object expiration_redisTime = stringRedisTemplate.opsForHash().get(username, "expiration");

                            Date date;
                            try {
                                date = (Date) f.parseObject(String.valueOf(expiration_redisTime));
                            } catch (ParseException ex) {
                                throw new RuntimeException(ex);
                            }
                            //通過數(shù)據(jù)庫(kù)查詢數(shù)據(jù),創(chuàng)建token
                            System.out.println("這里之前開始的時(shí)間:" + date);
                            String token = TokenUtil.createToken(user, date);
                            System.out.println("—————————————————start—————————————————————");
                            System.out.println("token:" + token);
                            RedisUtil.redis_SaveTokenInfo(user, token);
                            response.setHeader(TokenUtil.TOKEN_HEAD, TokenUtil.TOKEN_PREFIX + token);
                            request.setAttribute(TokenUtil.TOKEN_HEAD, TokenUtil.TOKEN_PREFIX + token);
                            Date expiration1 = new TokenUtil().parseJwt(token).getExpiration();
                            System.out.println("重新更新token后過期時(shí)間:" + expiration1);
                            System.out.println("—————————————————End—————————————————————");
                            ResponseUtil.reponseOutDiy(response, 200, token);
                            System.out.println("*********Token過期(End)新Token***********");
                            return;
                        }
                    } else {
                        //TODO 新增,如果redis沒有username,說明未登錄
                        throw new RuntimeException("未登錄");
                    }
                }
            }
            //避免每次請(qǐng)求都請(qǐng)求數(shù)據(jù)庫(kù)查詢用戶信息,從緩存中查詢
            user = JSONObject.parseObject(String.valueOf(stringRedisTemplate.opsForHash().get(username, "user")), User.class);
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                if (user != null) {
                    UsernamePasswordAuthenticationToken authentication =
                            // TODO 未修改  這里的權(quán)限先空著
                            new UsernamePasswordAuthenticationToken(user, user.getPassword(), null);
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }
        System.out.println("走過濾——————————————————————————");
        chain.doFilter(request, response);
    }
}

5.5?LoginAuthenticationEntryPoint(AuthenticationEntryPoint)

import com.Lino_white.common.ResponseUtil;
import com.Lino_white.common.TokenUtil;
import jdk.nashorn.internal.parser.Token;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 匿名未登錄的時(shí)候訪問,需要登錄的資源的調(diào)用類
 * @author Lino_white
 */
@Component
public class LoginAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        String token =httpServletRequest.getHeader(TokenUtil.TOKEN_HEAD);
        System.out.println("當(dāng)前未登錄,無法訪問 ::"+token);
        if (token!=null && token.contains(TokenUtil.TOKEN_PREFIX)) {
            token=token.replace(TokenUtil.TOKEN_PREFIX,"");
            String usernameFromToken = new TokenUtil().getUsernameFromToken(token);
            System.out.println("用戶名:"+usernameFromToken);
        }
        ResponseUtil.reponseOutDiy(httpServletResponse,401,"當(dāng)前未登錄,無法訪問");
    }
}

5.6?LoginInFailHandler(AuthenticationFailureHandler)

import com.Lino_white.common.ResponseUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 登錄賬號(hào)密碼錯(cuò)誤等情況下,會(huì)調(diào)用的處理類
 * @author Lino_white
 */
@Component
public class LoginInFailHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        System.out.println("認(rèn)證失敗————————————");
        ResponseUtil.reponseOutDiy(httpServletResponse,401,"登錄失敗,請(qǐng)重試");
    }
}

5.7?LoginInSuccessHandler(AuthenticationSuccessHandler)

package com.goyes.spring_security.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.goyes.common.utils.RedisUtil;
import com.goyes.common.utils.ResponseUtil;
import com.goyes.common.utils.TokenUtil;
import com.goyes.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
 * @LoginInSuccessHandler.java的作用:
 * 登錄成功處理類,登錄成功后會(huì)調(diào)用里面的方法
 * @author: white文
 * @time: 2023/5/18 16:02
 */
@Slf4j
@Component
public class LoginInSuccessHandler implements AuthenticationSuccessHandler {


    /**
     * 用戶通過TokenLoginFilter(UsernamePasswordAuthenticationFilter)后,
     * 驗(yàn)證成功到這里進(jìn)行
     * 1.獲取當(dāng)前用戶
     * 2.token創(chuàng)建
     * 3.并將其存入redis并返回
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        log.info("登錄成功,開始初始化token并緩存在redis");
                User user =(User) authentication.getPrincipal();
        String token = TokenUtil.createToken(user);
        //redis緩存token
        RedisUtil.redis_SaveTokenInfo(user,token);
        //寫入response
        response.setHeader("token", TokenUtil.TOKEN_PREFIX+token);

        try {
            //登錄成功,返回json格式進(jìn)行提示
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter out=response.getWriter();
            Map<String,Object> map=new HashMap<String,Object>(4);
            map.put("code",HttpServletResponse.SC_OK);
            map.put("message","這里全部都是自定義的!登錄成功");
            map.put("token",token);
            out.write(new ObjectMapper().writeValueAsString(map));
            out.flush();
            out.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

5.8?LogOutSuccessHandler(LogoutSuccessHandler)

import com.Lino_white.common.RedisUtil;
import com.Lino_white.common.ResponseUtil;
import com.Lino_white.common.TokenUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class LogOutSuccessHandler implements LogoutSuccessHandler {
    private StringRedisTemplate stringRedisTemplate= RedisUtil.stringRedisTemplateStatic;
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //用戶退出登錄
        System.out.println("LogoutSuccessHandler退出");
        String token=request.getHeader("token");
        if (token==null) token=request.getHeader(TokenUtil.TOKEN_HEAD);
        token=token.replace(TokenUtil.TOKEN_PREFIX,"");
        String username = new TokenUtil().getUsernameFromToken(token);
        Authentication au = SecurityContextHolder.getContext().getAuthentication();
        if (au!=null) new SecurityContextLogoutHandler().logout(request,response,au);
        Boolean delete = stringRedisTemplate.delete(username);
        if (delete) ResponseUtil.reponseOutDiy(response,200,"用戶已成功退出");
    }
}

5.9?NothingAccessDeniedHandler(AccessDeniedHandler)

import com.Lino_white.common.ResponseUtil;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 沒有權(quán)限,被拒絕訪問時(shí)的調(diào)用類
 * @author Lino_white
 */
@Component
public class NothingAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        System.out.println("沒有權(quán)限");
        ResponseUtil.reponseOutDiy(httpServletResponse,403,"當(dāng)前您沒有該權(quán)限");
    }
}

5.10?MyUserDetailService(UserDetailsService)

注意:在這里調(diào)用了model模塊中的UserFeign文件,實(shí)現(xiàn)讀取service_user模塊中的findUserByName方法。

package com.goyes.spring_security.service;

import com.goyes.model.User;
import com.goyes.model.client.UserFeign;
import com.goyes.spring_security.model.DiyUserDetails;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * 從數(shù)據(jù)庫(kù)讀取用戶信息(用戶名,密碼,身份)進(jìn)行身份認(rèn)證
 */
@Service
public class MyUserDetailService implements UserDetailsService{
    @Autowired
    private UserFeign userFeign;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("********開始loadUserByUsername********");
        User user = userFeign.findUserByName(username);
        System.out.println("瀏覽器的username:"+username);

        if (user==null) throw new UsernameNotFoundException(username);
        System.out.println("數(shù)據(jù)庫(kù)的username:"+user.getUsername());
        //根據(jù)當(dāng)前用戶名查詢用戶權(quán)限
        List<String> authorities=new ArrayList<>();
        authorities.add("ROLE_"+user.getIdentity());
        DiyUserDetails details=new DiyUserDetails();
        BeanUtils.copyProperties(user,details);
        details.setAuthorities(authorities);
        //如果數(shù)據(jù)庫(kù)密碼無加密,用下列
        //details.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));
        System.out.println("********結(jié)束loadUserByUsername********");
        return details;
    }
}

6.gateway模塊

6.1 pom

        <dependency>
           <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

6.2 application.yml

server:
  port: 10000
spring:
  application:
    name: gateway

  cloud:
    gateway:
      routes:
        - id: user
          uri: http://localhost:8001
          predicates:
            - Path=/user/**,/admin/**,/api/user/**,/login,/logout
        - id: course
          uri: http://localhost:8002
          predicates:
            - Path=/course/**

7.測(cè)試

這里是使用postman工具進(jìn)行測(cè)試的,在這里之前已經(jīng)在數(shù)據(jù)庫(kù)有用戶名跟密碼都為111的數(shù)據(jù),并且密碼已是加密形式。

1.首次訪問/admin/findAll,gateway會(huì)調(diào)用到8001端口下的user模塊,如下圖

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

?首次訪問/course/findAll,gateway會(huì)調(diào)用到8002端口下的course模塊,如下圖

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

2.POST訪問/login,并且提供相關(guān)參數(shù)(數(shù)據(jù)庫(kù)存在用戶111和加密過的密碼111),得到token,如下圖:

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

這時(shí),redis數(shù)據(jù)庫(kù)就有了用戶名為 user的數(shù)據(jù)?

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

3.復(fù)制剛才返回給前端的token,在Authorization的Type中,選擇Bearer Token,粘貼上剛才的Token,點(diǎn)擊Send發(fā)送

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

?5.再次請(qǐng)求8002端口下的source模塊

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

?再次請(qǐng)求8001端口下的user模塊

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

6.當(dāng)token過期后,redis中的異常時(shí)間還沒到,則會(huì)返回給前端一個(gè)新的token,拿著新token繼續(xù)請(qǐng)求即可。

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

7.當(dāng)token過期并且redis的異常時(shí)間也過了之后,用戶就需要重新登錄。

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

8.退出則為/logout 。

微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你

??同時(shí),redis數(shù)據(jù)庫(kù)中用戶名為user的key值也被刪除掉。

至此,結(jié)束!祝大家520快樂?。?!文章來源地址http://www.zghlxwxcb.cn/news/detail-451827.html

到了這里,關(guān)于微服務(wù)多模塊:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源碼)僅需一招,520徹底拿捏你的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • SpringCloud微服務(wù)之間如何進(jìn)行用戶信息傳遞(涉及:Gateway、OpenFeign組件)

    SpringCloud微服務(wù)之間如何進(jìn)行用戶信息傳遞(涉及:Gateway、OpenFeign組件)

    在業(yè)務(wù)微服務(wù)中通過工具類獲取當(dāng)前用戶信息 網(wǎng)關(guān)微服務(wù)(Gateway)往業(yè)務(wù)微服務(wù)傳遞用戶信息 業(yè)務(wù)微服務(wù)之間通過OpenFeign傳遞用戶信息 只要把上面兩處打通,然后業(yè)務(wù)微服務(wù)在通過攔截器獲取到用戶信息,之后再將用戶信息存在ThreadLocal中,這樣我們就可以實(shí)現(xiàn)在業(yè)務(wù)微服

    2024年02月13日
    瀏覽(22)
  • 研發(fā)提速:nacos+openfeign環(huán)境下的本地鏈接服務(wù)

    項(xiàng)目研發(fā)過程中,經(jīng)常會(huì)遇到與測(cè)試人員工作重疊的情況,十分影響效率。 做了一個(gè)修改,可以在本地環(huán)境啟動(dòng)項(xiàng)目后和測(cè)試環(huán)境交互,并且不影響測(cè)試環(huán)境,理論上也可以用于線上環(huán)境的異常的快速處理。 準(zhǔn)備事項(xiàng)如下: 一:搭建本地的nacos服務(wù)。 二:導(dǎo)入測(cè)試環(huán)境相應(yīng)

    2024年02月08日
    瀏覽(20)
  • springboot-gateway注冊(cè)nacos失敗,控制臺(tái)沒有報(bào)錯(cuò)

    springboot-gateway注冊(cè)nacos失敗,控制臺(tái)沒有報(bào)錯(cuò)

    最近springboot的gateway注冊(cè)到nacos上,沒有注冊(cè)成功 我是在common里面引入了nacos的依賴,依賴如下: 然后代碼yml里面添加如下:

    2023年04月09日
    瀏覽(25)
  • 微服務(wù)動(dòng)態(tài)權(quán)限管理方案(Spring Cloud Gateway+Spring Cloud Security)

    微服務(wù)動(dòng)態(tài)權(quán)限管理方案(Spring Cloud Gateway+Spring Cloud Security)

    微服務(wù)認(rèn)證方案的大體方向是統(tǒng)一在網(wǎng)關(guān)層面認(rèn)證鑒權(quán),微服務(wù)只負(fù)責(zé)業(yè)務(wù),和鑒權(quán)完全隔離 整體包含以下四個(gè)角色 客戶端 :需要訪問微服務(wù)資源 網(wǎng)關(guān) :負(fù)責(zé)轉(zhuǎn)發(fā)、認(rèn)證、鑒權(quán) OAuth2.0授權(quán)服務(wù) :負(fù)責(zé)認(rèn)證授權(quán)頒發(fā)令牌 微服務(wù)集合 :提供資源的一系列服務(wù)。 這里的客戶端

    2024年02月12日
    瀏覽(21)
  • Gateway服務(wù)集成Nacos2021.0.4錯(cuò)誤解決

    Gateway服務(wù)集成Nacos2021.0.4錯(cuò)誤解決

    gateway服務(wù)集成nacos,啟動(dòng)后報(bào)錯(cuò): Caused by: com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: no further information: ; 版本: jdk:1.8 spring-boot:2.7.11 spring-cloud:2021.0.6 spring-cloud-alibaba:2021.0.4.0 單配置文件:application.yaml中配置 多配置文件:

    2024年02月13日
    瀏覽(13)
  • 若依框架解讀(微服務(wù)版)——2.模塊間的調(diào)用邏輯(ruoyi-api模塊)(OpenFeign)(@innerAuth)

    若依框架解讀(微服務(wù)版)——2.模塊間的調(diào)用邏輯(ruoyi-api模塊)(OpenFeign)(@innerAuth)

    我們可以了解到一共有這么多服務(wù),我們先啟動(dòng)這三個(gè)服務(wù) 其中rouyi–api模塊是遠(yuǎn)程調(diào)用也就是提取出來的openfeign的接口 ruoyi–commom是通用工具模塊 其他幾個(gè)都是獨(dú)立的服務(wù) api模塊當(dāng)中有幾個(gè)提取出來的OpenFeign的接口 分別為文件,日志,用戶服務(wù) 我們以RemoteUserService接口為

    2023年04月09日
    瀏覽(50)
  • Spring Security Oauth2.1 最新版 1.1.0 整合 gateway 完成授權(quán)認(rèn)證(擁抱 springboot 3.1)

    Spring Security Oauth2.1 最新版 1.1.0 整合 gateway 完成授權(quán)認(rèn)證(擁抱 springboot 3.1)

    目錄 背景 demo地址 版本 Spring Boot 3.1 Spring Authorization Server 1.1.0 基礎(chǔ) spring security OAuth2 模塊構(gòu)成 授權(quán)方式 認(rèn)證方式 集成過程 官方demo 代碼集成 依賴 授權(quán)服務(wù)AuthorizationServerConfig配置 重要組件 測(cè)試 查看授權(quán)服務(wù)配置 訪問授權(quán)服務(wù) 授權(quán) 回調(diào) 獲取?access_token 獲取用戶信息 個(gè)性

    2024年02月08日
    瀏覽(19)
  • Nacos配置管理、Fegin遠(yuǎn)程調(diào)用、Gateway服務(wù)網(wǎng)關(guān)

    Nacos配置管理、Fegin遠(yuǎn)程調(diào)用、Gateway服務(wù)網(wǎng)關(guān)

    Nacos除了可以做注冊(cè)中心,同樣可以做配置管理來使用。 當(dāng)微服務(wù)部署的實(shí)例越來越多,達(dá)到數(shù)十、數(shù)百時(shí),逐個(gè)修改微服務(wù)配置就會(huì)讓人抓狂,而且很容易出錯(cuò)。我們需要一種統(tǒng)一配置管理方案,可以集中管理所有實(shí)例的配置。 Nacos一方面可以將配置集中管理,另一方可以

    2024年02月03日
    瀏覽(14)
  • springcloud gateway實(shí)時(shí)監(jiān)聽nacos微服務(wù)上下線

    Nacos : 1.3.1 SpringCloud : 2021.0.2 SpringCloud gateway : 3.1.2 微服務(wù)下線后,網(wǎng)關(guān)存在短時(shí)間內(nèi)轉(zhuǎn)發(fā)失效服務(wù),導(dǎo)致前端訪問異常 微服務(wù)上線后,網(wǎng)關(guān)沒有及時(shí)刷新本地緩存的服務(wù),導(dǎo)致前端可能找不到服務(wù)實(shí)例 nacos的主動(dòng)推送實(shí)例變化比網(wǎng)關(guān)自己拉取要及時(shí)的多 此處配置注意點(diǎn): 1、

    2024年02月08日
    瀏覽(17)
  • Nacos配置管理、Feign遠(yuǎn)程調(diào)用、Gateway服務(wù)網(wǎng)關(guān)

    Nacos配置管理、Feign遠(yuǎn)程調(diào)用、Gateway服務(wù)網(wǎng)關(guān)

    1.在Nacos中添加配置 Data Id = 服務(wù)名稱-環(huán)境名稱.yaml eg: userservice-dev.yaml 2.引入nacos-config依賴 在user-service服務(wù)中,引入nacos-config的客戶端依賴 3.添加 bootstrap.yaml ,我們這里以顯示一個(gè)日期時(shí)間為例 在user-service中添加一個(gè)bootstrap.yaml文件 4.編寫controller 方式一 給 @Value 注入的變量

    2024年02月12日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包