欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot集成Spring Security實(shí)現(xiàn)JWT認(rèn)證的步驟詳解

 更新時(shí)間:2021年02月06日 17:30:01   作者:南瓜慢說(shuō)  
這篇文章主要介紹了Springboot集成Spring Security實(shí)現(xiàn)JWT認(rèn)證的步驟詳解,幫助大家更好的理解和使用springboot,感興趣的朋友可以了解下

1 簡(jiǎn)介

Spring Security作為成熟且強(qiáng)大的安全框架,得到許多大廠的青睞。而作為前后端分離的SSO方案,JWT也在許多項(xiàng)目中應(yīng)用。本文將介紹如何通過(guò)Spring Security實(shí)現(xiàn)JWT認(rèn)證。

用戶與服務(wù)器交互大概如下:

  1. 客戶端獲取JWT,一般通過(guò)POST方法把用戶名/密碼傳給server;
  2. 服務(wù)端接收到客戶端的請(qǐng)求后,會(huì)檢驗(yàn)用戶名/密碼是否正確,如果正確則生成JWT并返回;不正確則返回錯(cuò)誤;
  3. 客戶端拿到JWT后,在有效期內(nèi)都可以通過(guò)JWT來(lái)訪問資源了,一般把JWT放在請(qǐng)求頭;一次獲取,多次使用;
  4. 服務(wù)端校驗(yàn)JWT是否合法,合法則允許客戶端正常訪問,不合法則返回401。

2 項(xiàng)目整合

我們把要整合的Spring Security和JWT加入到項(xiàng)目的依賴中去:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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.1 JWT整合

2.1.1 JWT工具類

JWT工具類起碼要具有以下功能:

  • 根據(jù)用戶信息生成JWT;
  • 校驗(yàn)JWT是否合法,如是否被篡改、是否過(guò)期等;
  • 從JWT中解析用戶信息,如用戶名、權(quán)限等;

具體代碼如下:

@Component
public class JwtTokenProvider {

 @Autowired JwtProperties jwtProperties;

 @Autowired
 private CustomUserDetailsService userDetailsService;

 private String secretKey;

 @PostConstruct
 protected void init() {
  secretKey = Base64.getEncoder().encodeToString(jwtProperties.getSecretKey().getBytes());
 }

 public String createToken(String username, List<String> roles) {

  Claims claims = Jwts.claims().setSubject(username);
  claims.put("roles", roles);

  Date now = new Date();
  Date validity = new Date(now.getTime() + jwtProperties.getValidityInMs());

  return Jwts.builder()//
    .setClaims(claims)//
    .setIssuedAt(now)//
    .setExpiration(validity)//
    .signWith(SignatureAlgorithm.HS256, secretKey)//
    .compact();
 }

 public Authentication getAuthentication(String token) {
  UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token));
  return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
 }

 public String getUsername(String token) {
  return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
 }

 public String resolveToken(HttpServletRequest req) {
  String bearerToken = req.getHeader("Authorization");
  if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
   return bearerToken.substring(7);
  }
  return null;
 }

 public boolean validateToken(String token) {
  try {
   Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);

   if (claims.getBody().getExpiration().before(new Date())) {
    return false;
   }

   return true;
  } catch (JwtException | IllegalArgumentException e) {
   throw new InvalidJwtAuthenticationException("Expired or invalid JWT token");
  }
 }

}

工具類還實(shí)現(xiàn)了另一個(gè)功能:從HTTP請(qǐng)求頭中獲取JWT。

2.1.2 Token處理的Filter

Filter是Security處理的關(guān)鍵,基本上都是通過(guò)Filter來(lái)攔截請(qǐng)求的。首先從請(qǐng)求頭取出JWT,然后校驗(yàn)JWT是否合法,如果合法則取出Authentication保存在SecurityContextHolder里。如果不合法,則做異常處理。

public class JwtTokenAuthenticationFilter extends GenericFilterBean {

 private JwtTokenProvider jwtTokenProvider;

 public JwtTokenAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
  this.jwtTokenProvider = jwtTokenProvider;
 }

 @Override
 public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
   throws IOException, ServletException {
  HttpServletRequest request = (HttpServletRequest) req;
  HttpServletResponse response = (HttpServletResponse) res;

  try {
   String token = jwtTokenProvider.resolveToken(request);
   if (token != null && jwtTokenProvider.validateToken(token)) {
    Authentication auth = jwtTokenProvider.getAuthentication(token);

    if (auth != null) {
     SecurityContextHolder.getContext().setAuthentication(auth);
    }
   }
  } catch (InvalidJwtAuthenticationException e) {
   response.setStatus(HttpStatus.UNAUTHORIZED.value());
   response.getWriter().write("Invalid token");
   response.getWriter().flush();
   return;
  }

  filterChain.doFilter(req, res);
 }
}

對(duì)于異常處理,使用@ControllerAdvice是不行的,應(yīng)該這個(gè)是Filter,在這里拋的異常還沒有到DispatcherServlet,無(wú)法處理。所以Filter要自己做異常處理:

catch (InvalidJwtAuthenticationException e) {
 response.setStatus(HttpStatus.UNAUTHORIZED.value());
 response.getWriter().write("Invalid token");
 response.getWriter().flush();
 return;
}

最后的return不能省略,因?yàn)橐呀?jīng)要把輸出的內(nèi)容給Response了,沒有必要再往后傳遞,否則報(bào)錯(cuò)

java.lang.IllegalStateException: getWriter() has already been called

2.1.3 JWT屬性

JWT需要配置一個(gè)密鑰來(lái)加密,同時(shí)還要配置JWT令牌的有效期。

@Configuration
@ConfigurationProperties(prefix = "pkslow.jwt")
public class JwtProperties {
 private String secretKey = "pkslow.key";
 private long validityInMs = 3600_000;
//getter and setter
}

2.2 Spring Security整合

Spring Security的整個(gè)框架還是比較復(fù)雜的,簡(jiǎn)化后大概如下圖所示:

它是通過(guò)一連串的Filter來(lái)進(jìn)行安全管理。細(xì)節(jié)這里先不展開講。

2.2.1 WebSecurityConfigurerAdapter配置

這個(gè)配置也可以理解為是FilterChain的配置,可以不用理解,代碼很好懂它做了什么:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


 @Autowired
 JwtTokenProvider jwtTokenProvider;

 @Bean
 @Override
 public AuthenticationManager authenticationManagerBean() throws Exception {
  return super.authenticationManagerBean();
 }

 @Bean
 public PasswordEncoder passwordEncoder() {
  return NoOpPasswordEncoder.getInstance();
 }

 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http
   .httpBasic().disable()
   .csrf().disable()
   .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
   .and()
   .authorizeRequests()
   .antMatchers("/auth/login").permitAll()
   .antMatchers(HttpMethod.GET, "/admin").hasRole("ADMIN")
   .antMatchers(HttpMethod.GET, "/user").hasRole("USER")
   .anyRequest().authenticated()
   .and()
   .apply(new JwtSecurityConfigurer(jwtTokenProvider));
 }
}

這里通過(guò)HttpSecurity配置了哪些請(qǐng)求需要什么權(quán)限才可以訪問。

  • /auth/login用于登陸獲取JWT,所以都能訪問;
  • /admin只有ADMIN用戶才可以訪問;
  • /user只有USER用戶才可以訪問。

而之前實(shí)現(xiàn)的Filter則在下面配置使用:

public class JwtSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

 private JwtTokenProvider jwtTokenProvider;

 public JwtSecurityConfigurer(JwtTokenProvider jwtTokenProvider) {
  this.jwtTokenProvider = jwtTokenProvider;
 }

 @Override
 public void configure(HttpSecurity http) throws Exception {
  JwtTokenAuthenticationFilter customFilter = new JwtTokenAuthenticationFilter(jwtTokenProvider);
  http.exceptionHandling()
    .authenticationEntryPoint(new JwtAuthenticationEntryPoint())
    .and()
    .addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
 }
}

2.2.2 用戶從哪來(lái)

通常在Spring Security的世界里,都是通過(guò)實(shí)現(xiàn)UserDetailsService來(lái)獲取UserDetails的。

@Component
public class CustomUserDetailsService implements UserDetailsService {

 private UserRepository users;

 public CustomUserDetailsService(UserRepository users) {
  this.users = users;
 }

 @Override
 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  return this.users.findByUsername(username)
    .orElseThrow(() -> new UsernameNotFoundException("Username: " + username + " not found"));
 }
}

對(duì)于UserRepository,可以從數(shù)據(jù)庫(kù)中讀取,或者其它用戶管理中心。為了方便,我使用Map放了兩個(gè)用戶:

@Repository
public class UserRepository {

 private static final Map<String, User> allUsers = new HashMap<>();

 @Autowired
 private PasswordEncoder passwordEncoder;

 @PostConstruct
 protected void init() {
  allUsers.put("pkslow", new User("pkslow", passwordEncoder.encode("123456"), Collections.singletonList("ROLE_ADMIN")));
  allUsers.put("user", new User("user", passwordEncoder.encode("123456"), Collections.singletonList("ROLE_USER")));
 }

 public Optional<User> findByUsername(String username) {
  return Optional.ofNullable(allUsers.get(username));
 }
}

3 測(cè)試

完成代碼編寫后,我們來(lái)測(cè)試一下:

(1)無(wú)JWT訪問,失敗

curl http://localhost:8080/admin
{"timestamp":"2021-02-06T05:45:06.385+0000","status":403,"error":"Forbidden","message":"Access Denied","path":"/admin"}

$ curl http://localhost:8080/user
{"timestamp":"2021-02-06T05:45:16.438+0000","status":403,"error":"Forbidden","message":"Access Denied","path":"/user"}

(2)admin獲取JWT,密碼錯(cuò)誤則失敗,密碼正確則成功

$ curl http://localhost:8080/auth/login -X POST -d '{"username":"pkslow","password":"xxxxxx"}' -H 'Content-Type: application/json'
{"timestamp":"2021-02-06T05:47:16.254+0000","status":403,"error":"Forbidden","message":"Access Denied","path":"/auth/login"}

$ curl http://localhost:8080/auth/login -X POST -d '{"username":"pkslow","password":"123456"}' -H 'Content-Type: application/json'
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwa3Nsb3ciLCJyb2xlcyI6WyJST0xFX0FETUlOIl0sImlhdCI6MTYxMjU5MDYxNCwiZXhwIjoxNjEyNTkxMjE0fQ.d4Gi50aaOsHHqpM0d8Mh1960otnZf7rlE3x6xSfakVo 

(3)admin帶JWT訪問/admin,成功;訪問/user失敗

$ curl http://localhost:8080/admin -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwa3Nsb3ciLCJyb2xlcyI6WyJST0xFX0FETUlOIl0sImlhdCI6MTYxMjU5MDYxNCwiZXhwIjoxNjEyNTkxMjE0fQ.d4Gi50aaOsHHqpM0d8Mh1960otnZf7rlE3x6xSfakVo'
you are admin

$ curl http://localhost:8080/user -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwa3Nsb3ciLCJyb2xlcyI6WyJST0xFX0FETUlOIl0sImlhdCI6MTYxMjU5MDYxNCwiZXhwIjoxNjEyNTkxMjE0fQ.d4Gi50aaOsHHqpM0d8Mh1960otnZf7rlE3x6xSfakVo'
{"timestamp":"2021-02-06T05:51:23.099+0000","status":403,"error":"Forbidden","message":"Forbidden","path":"/user"}

(4)使用過(guò)期的JWT訪問,失敗

$ curl http://localhost:8080/admin -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwa3Nsb3ciLCJyb2xlcyI6WyJST0xFX0FETUlOIl0sImlhdCI6MTYxMjU5MDQ0OSwiZXhwIjoxNjEyNTkwNTA5fQ.CSaubE4iJcYATbLmbb59aNFU1jNCwDFHUV3zIakPU64'
Invalid token

4 總結(jié)

代碼請(qǐng)查看:https://github.com/LarryDpk/pkslow-samples

以上就是Springboot集成Spring Security實(shí)現(xiàn)JWT認(rèn)證的步驟詳解的詳細(xì)內(nèi)容,更多關(guān)于Springboot集成Spring Security的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • springboot jpa分庫(kù)分表項(xiàng)目實(shí)現(xiàn)過(guò)程詳解

    springboot jpa分庫(kù)分表項(xiàng)目實(shí)現(xiàn)過(guò)程詳解

    這篇文章主要介紹了springboot jpa分庫(kù)分表項(xiàng)目實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • springboot實(shí)現(xiàn)圖片上傳與下載功能

    springboot實(shí)現(xiàn)圖片上傳與下載功能

    這篇文章主要為大家詳細(xì)介紹了后端spring項(xiàng)目經(jīng)常要做的功能,實(shí)現(xiàn)圖片上傳和下載,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-12-12
  • SpringBoot中使用攔截器的配置詳解

    SpringBoot中使用攔截器的配置詳解

    這篇文章主要介紹了SpringBoot中使用攔截器的配置詳解,攔截器是?AOP?的一種實(shí)現(xiàn),專門攔截對(duì)動(dòng)態(tài)資源的后臺(tái)請(qǐng)求,即攔截對(duì)控制層的請(qǐng)?求,使用場(chǎng)景比較多的是判斷用戶是否有權(quán)限請(qǐng)求后臺(tái),需要的朋友可以參考下
    2024-01-01
  • IDEA 2021.1 的 Win 和 Mac 快捷鍵大全

    IDEA 2021.1 的 Win 和 Mac 快捷鍵大全

    這篇文章主要介紹了IDEA 2021.1 的 Win 和 Mac 快捷鍵大全,本文是小編給大家精心收藏的,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • 解決redisTemplate中l(wèi)eftPushAll隱性bug的問題

    解決redisTemplate中l(wèi)eftPushAll隱性bug的問題

    這篇文章主要介紹了解決redisTemplate中l(wèi)eftPushAll隱性bug的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • spring?boot獲取session的值為null問題及解決方法

    spring?boot獲取session的值為null問題及解決方法

    我在登陸的時(shí)候,登陸成功后將name存進(jìn)了session,然后在獲取個(gè)人信息時(shí)取出session里的name的值為null,接下來(lái)通過(guò)本文給大家分享springboot獲取session的值為null問題,需要的朋友可以參考下
    2023-05-05
  • Java快速批量移動(dòng)文件的實(shí)現(xiàn)方法

    Java快速批量移動(dòng)文件的實(shí)現(xiàn)方法

    這篇文章主要介紹了Java快速批量移動(dòng)文件的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2014-03-03
  • springboot注解及GET、POST接口寫法

    springboot注解及GET、POST接口寫法

    springboot提供了@Contrller和@RestController注解,@Controller返回頁(yè)面和數(shù)據(jù)而@RestController返回?cái)?shù)據(jù),本文重點(diǎn)介紹springboot注解及GET、POST接口寫法,感興趣的朋友一起看看吧
    2024-04-04
  • Spring Cloud中配置客戶端示例詳解

    Spring Cloud中配置客戶端示例詳解

    這篇文章主要介紹了Spring Cloud中配置客戶端的相關(guān)知識(shí),本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • Mybatis-plus批量插入的2種方式總結(jié)

    Mybatis-plus批量插入的2種方式總結(jié)

    這篇文章主要給大家總結(jié)介紹了關(guān)于Mybatis-plus批量插入的2種方式,Mybatis-Plus提供了多種方式進(jìn)行批量插入優(yōu)化,文中通過(guò)代碼示例將實(shí)現(xiàn)的方法介紹的非常詳細(xì),需要的朋友可以參考下
    2023-08-08

最新評(píng)論