最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot?SpringSecurity?JWT實現(xiàn)系統(tǒng)安全策略詳解

 更新時間:2022年11月20日 08:32:33   作者:Buckletime  
Spring?Security是Spring的一個核心項目,它是一個功能強大且高度可定制的認(rèn)證和訪問控制框架。它提供了認(rèn)證和授權(quán)功能以及抵御常見的攻擊,它已經(jīng)成為保護(hù)基于spring的應(yīng)用程序的事實標(biāo)準(zhǔn)

security進(jìn)行用戶驗證和授權(quán);jwt負(fù)責(zé)頒發(fā)令牌與校驗,判斷用戶登錄狀態(tài)

一、原理

1. SpringSecurity過濾器鏈

SpringSecurity 采用的是責(zé)任鏈的設(shè)計模式,它有一條很長的過濾器鏈。

  • SecurityContextPersistenceFilter:在每次請求處理之前將該請求相關(guān)的安全上下文信息加載到 SecurityContextHolder 中。
  • LogoutFilter:用于處理退出登錄。
  • UsernamePasswordAuthenticationFilter:用于處理基于表單的登錄請求,從表單中獲取用戶名和密碼。
  • BasicAuthenticationFilter:檢測和處理 http basic 認(rèn)證。
  • ExceptionTranslationFilter:處理 AccessDeniedException 和 AuthenticationException 異常。
  • FilterSecurityInterceptor:可以看做過濾器鏈的出口。

流程說明:客戶端發(fā)起一個請求,進(jìn)入 Security 過濾器鏈。

當(dāng)?shù)?LogoutFilter 的時候判斷是否是登出路徑,如果是登出路徑則到 logoutHandler ,如果登出成功則到 logoutSuccessHandler 登出成功處理,如果登出失敗則由 ExceptionTranslationFilter ;如果不是登出路徑則直接進(jìn)入下一個過濾器。

當(dāng)?shù)?UsernamePasswordAuthenticationFilter 的時候判斷是否為登錄路徑,如果是,則進(jìn)入該過濾器進(jìn)行登錄操作,如果登錄失敗則到 AuthenticationFailureHandler 登錄失敗處理器處理,如果登錄成功則到 AuthenticationSuccessHandler 登錄成功處理器處理,如果不是登錄請求則不進(jìn)入該過濾器。

當(dāng)?shù)?FilterSecurityInterceptor 的時候會拿到 uri ,根據(jù) uri 去找對應(yīng)的鑒權(quán)管理器,鑒權(quán)管理器做鑒權(quán)工作,鑒權(quán)成功則到 Controller 層否則到 AccessDeniedHandler 鑒權(quán)失敗處理器處理。

2. JWT校驗

首先前端一樣是把登錄信息發(fā)送給后端,后端查詢數(shù)據(jù)庫校驗用戶的賬號和密碼是否正確,正確的話則使用jwt生成token,并且返回給前端。以后前端每次請求時,都需要攜帶token,后端獲取token后,使用jwt進(jìn)行驗證用戶的token是否無效或過期,驗證成功后才去做相應(yīng)的邏輯。

二、Security+JWT配置說明

1. 添加maven依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2. securityConfig配置

/**
 * Security 配置
 */
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    LoginFailureHandler loginFailureHandler;
    @Autowired
    LoginSuccessHandler loginSuccessHandler;
    @Autowired
    JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    @Autowired
    JwtAccessDeniedHandler jwtAccessDeniedHandler;
    @Autowired
    UserDetailServiceImpl userDetailService;
    @Autowired
    JWTLogoutSuccessHandler jwtLogoutSuccessHandler;
    @Autowired
    CaptchaFilter captchaFilter;
    @Value("${security.enable}")
    private Boolean securityIs = Boolean.TRUE;
    @Value("${security.permit}")
    private String permit;
    @Bean
    public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
        StrictHttpFirewall firewall = new StrictHttpFirewall();
        //此處可添加別的規(guī)則,目前只設(shè)置 允許雙 //
        firewall.setAllowUrlEncodedDoubleSlash(true);
        return firewall;
    }
    @Bean
    JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager(), jwtAuthenticationEntryPoint);
        return jwtAuthenticationFilter;
    }
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.cors().and().csrf().disable()
                // 登錄配置
                .formLogin()
                .successHandler(loginSuccessHandler)
                .failureHandler(loginFailureHandler)
                .and()
                .logout()
                .logoutSuccessHandler(jwtLogoutSuccessHandler)
                // 禁用session
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // 配置攔截規(guī)則
                .and()
                .authorizeRequests()
                .antMatchers(permit.split(",")).permitAll();
        if (!securityIs) {
            http.authorizeRequests().antMatchers("/**").permitAll();
        }
        registry.anyRequest().authenticated()
                // 異常處理器
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .accessDeniedHandler(jwtAccessDeniedHandler)
                // 配置自定義的過濾器
                .and()
                .addFilter(jwtAuthenticationFilter())
                // 驗證碼過濾器放在UsernamePassword過濾器之前
                .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class);
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService);
    }
}

3. JwtAuthenticationFilter校驗token

package cn.piesat.gf.filter;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.piesat.gf.dao.user.SysUserDao;
import cn.piesat.gf.model.entity.user.SysUser;
import cn.piesat.gf.exception.ExpiredAuthenticationException;
import cn.piesat.gf.exception.MyAuthenticationException;
import cn.piesat.gf.service.user.impl.UserDetailServiceImpl;
import cn.piesat.gf.utils.Constants;
import cn.piesat.gf.utils.JwtUtils;
import cn.piesat.gf.utils.Result;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Slf4j
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
    private AuthenticationEntryPoint authenticationEntryPoint;
    private AuthenticationManager authenticationManager;
    @Autowired
    JwtUtils jwtUtils;
    @Autowired
    UserDetailServiceImpl userDetailService;
    @Autowired
    SysUserDao sysUserRepository;
    @Autowired
    RedisTemplate redisTemplate;
    @Value("${security.single}")
    private Boolean singleLogin = false;
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
        super(authenticationManager, authenticationEntryPoint);
        Assert.notNull(authenticationManager, "authenticationManager cannot be null");
        Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null");
        this.authenticationManager = authenticationManager;
        this.authenticationEntryPoint = authenticationEntryPoint;
    }
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String jwt = request.getHeader(jwtUtils.getHeader());
        // 這里如果沒有jwt,繼續(xù)往后走,因為后面還有鑒權(quán)管理器等去判斷是否擁有身份憑證,所以是可以放行的
        // 沒有jwt相當(dāng)于匿名訪問,若有一些接口是需要權(quán)限的,則不能訪問這些接口
        if (StrUtil.isBlankOrUndefined(jwt)) {
            chain.doFilter(request, response);
            return;
        }
        try {
            Claims claim = jwtUtils.getClaimsByToken(jwt);
            if (claim == null) {
                throw new MyAuthenticationException("token 異常");
            }
            if (jwtUtils.isTokenExpired(claim)) {
                throw new MyAuthenticationException("token 已過期");
            }
            String username = claim.getSubject();
            Object o1 = redisTemplate.opsForValue().get(Constants.TOKEN_KEY + username);
            String o = null;
            if(!ObjectUtils.isEmpty(o1)){
                o = o1.toString();
            }
            if (!StringUtils.hasText(o)) {
                throw new ExpiredAuthenticationException("您的登錄信息已過期,請重新登錄!");
            }
            if (singleLogin && StringUtils.hasText(o) && !jwt.equals(o)) {
                throw new MyAuthenticationException("您的賬號已別處登錄,您已下線,如有異常請及時修改密碼!");
            }
            // 獲取用戶的權(quán)限等信息
            SysUser sysUser = sysUserRepository.findByUserName(username);
            // 構(gòu)建UsernamePasswordAuthenticationToken,這里密碼為null,是因為提供了正確的JWT,實現(xiàn)自動登錄
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, userDetailService.getUserAuthority(sysUser.getUserId()));
            SecurityContextHolder.getContext().setAuthentication(token);
            chain.doFilter(request, response);
        } catch (AuthenticationException e) {
            log.error(ExceptionUtil.stacktraceToString(e));
            authenticationEntryPoint.commence(request, response, e);
            return;
        } catch (Exception e){
            log.error(ExceptionUtil.stacktraceToString(e));
            response.getOutputStream().write(JSONUtil.toJsonStr(Result.fail(e.getMessage())).getBytes(StandardCharsets.UTF_8));
            response.getOutputStream().flush();
            response.getOutputStream().close();
            return;
        }
    }
}

4. JWT生成與解析工具類

package cn.piesat.gf.utils;
import cn.hutool.core.exceptions.ExceptionUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
@Data
@Component
@ConfigurationProperties(prefix = "jwt.config")
@Slf4j
public class JwtUtils {
    private long expire;
    private String secret;
    private String header;
    // 生成JWT
    public String generateToken(String username) {
        Date nowDate = new Date();
        Date expireDate = new Date(nowDate.getTime() + 1000 * expire);
        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setSubject(username)
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    // 解析JWT
    public Claims getClaimsByToken(String jwt) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(jwt)
                    .getBody();
        } catch (Exception e) {
            log.error(ExceptionUtil.stacktraceToString(e));
            return null;
        }
    }
    // 判斷JWT是否過期
    public boolean isTokenExpired(Claims claims) {
        return claims.getExpiration().before(new Date());
    }
}

到此這篇關(guān)于SpringBoot SpringSecurity JWT實現(xiàn)系統(tǒng)安全策略詳解的文章就介紹到這了,更多相關(guān)SpringBoot SpringSecurity JWT內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • spring-boot實現(xiàn)增加自定義filter(新)

    spring-boot實現(xiàn)增加自定義filter(新)

    本篇文章主要介紹了spring-boot實現(xiàn)增加自定義filter(新),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • Spring Boot啟動及退出加載項的方法

    Spring Boot啟動及退出加載項的方法

    這篇文章主要介紹了Spring Boot啟動及退出加載項的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • IDEA中application.properties的圖標(biāo)顯示不正常的問題及解決方法

    IDEA中application.properties的圖標(biāo)顯示不正常的問題及解決方法

    這篇文章主要介紹了IDEA中application.properties的圖標(biāo)顯示不正常的問題及解決方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • 如何用ObjectMapper將復(fù)雜Map轉(zhuǎn)換為實體類

    如何用ObjectMapper將復(fù)雜Map轉(zhuǎn)換為實體類

    這篇文章主要介紹了如何用ObjectMapper將復(fù)雜Map轉(zhuǎn)換為實體類的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 使用SpringBoot Actuator監(jiān)控應(yīng)用示例

    使用SpringBoot Actuator監(jiān)控應(yīng)用示例

    Actuator是Spring Boot提供的對應(yīng)用系統(tǒng)的自省和監(jiān)控的集成功能,可以對應(yīng)用系統(tǒng)進(jìn)行配置查看、相關(guān)功能統(tǒng)計等。這篇文章主要介紹了使用SpringBoot Actuator監(jiān)控應(yīng),有興趣的可以了解一下
    2018-05-05
  • java算法題解??虰M99順時針旋轉(zhuǎn)矩陣示例

    java算法題解??虰M99順時針旋轉(zhuǎn)矩陣示例

    這篇文章主要為大家介紹了java算法題解??虰M99順時針旋轉(zhuǎn)矩陣示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • java控制臺實現(xiàn)學(xué)生信息管理系統(tǒng)(集合版)

    java控制臺實現(xiàn)學(xué)生信息管理系統(tǒng)(集合版)

    這篇文章主要為大家詳細(xì)介紹了java控制臺實現(xiàn)學(xué)生信息管理系統(tǒng)的集合版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • 詳解Java引用類型的參數(shù)也是值傳遞

    詳解Java引用類型的參數(shù)也是值傳遞

    這篇文章主要介紹了Java引用類型的參數(shù)也是值傳遞,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • SpringCloud實戰(zhàn)小貼士之Zuul的路徑匹配

    SpringCloud實戰(zhàn)小貼士之Zuul的路徑匹配

    這篇文章主要介紹了SpringCloud實戰(zhàn)小貼士之Zuul的路徑匹配,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • Springboot中依賴注入的三種方式詳解

    Springboot中依賴注入的三種方式詳解

    這篇文章主要介紹了Springboot中依賴注入的三種方式詳解,Setter Injection需要依賴@Autowired注解,使用方式與Field Injection有所不同,Field Injection時@Autowired是用在成員變量上,需要的朋友可以參考下
    2023-09-09

最新評論

邵武市| 木兰县| 安义县| 拉萨市| 大埔区| 亚东县| 平泉县| 衡水市| 巩留县| 元氏县| 宜川县| 青河县| 开江县| 南丰县| 昌平区| 蓬溪县| 龙南县| 民和| 三河市| 周至县| 花垣县| 建平县| 澄江县| 佛山市| 无极县| 高青县| 囊谦县| 昆山市| 隆化县| 周口市| 克什克腾旗| 英吉沙县| 永新县| 江安县| 页游| 五指山市| 武清区| 大安市| 准格尔旗| 建始县| 乐亭县|