日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Security(一):整合JWT

發布時間:2025/7/25 javascript 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Security(一):整合JWT 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

違背的青春

今天寫下Spring Security整合jwt的一個簡單小Demo,目的是登錄后實現返回token,其實整個過程很簡單。

導入依賴

<dependency>
????<groupId>io.jsonwebtoken</groupId>
????<artifactId>jjwt</artifactId>
????<version>0.9.0</version>
</dependency>
<dependency>
????<groupId>org.springframework.boot</groupId>
????<artifactId>spring-boot-starter-security</artifactId>
</dependency>
復制代碼

首先創建一個JwtUser實現UserDetails

org.springframework.security.core.userdetails.UserDetails
先看一下這個接口的源碼,其實很簡單

public?interface?UserDetails?extends?Serializable?{
????Collection<??extends?GrantedAuthority>?getAuthorities();

????String?getPassword();

????String?getUsername();

????boolean?isAccountNonExpired();

????boolean?isAccountNonLocked();

????boolean?isCredentialsNonExpired();

????boolean?isEnabled();
}
復制代碼

這個是Spring Security給我們提供的一個簡單的接口,因為我們需要通過SecurityContextHolder去取得用戶憑證等等信息,因為這個比較簡單,所以我們實際業務要來加上我們所需要的信息。

public?class?JwtUser?implements?UserDetails?{


????private?String?username;

????private?String?password;

????private?Integer?state;

????private?Collection<??extends?GrantedAuthority>?authorities;

????public?JwtUser()?{
????}

????public?JwtUser(String?username,?String?password,?Integer?state,?Collection<??extends?GrantedAuthority>?authorities)?{
????????this.username?=?username;
????????this.password?=?password;
????????this.state?=?state;
????????this.authorities?=?authorities;
????}

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

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

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

????//賬戶是否未過期
????@JsonIgnore
????@Override
????public?boolean?isAccountNonExpired()?{
????????return?true;
????}

????//賬戶是否未被鎖
????@Override
????public?boolean?isAccountNonLocked()?{
????????return?true;
????}



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


????//是否啟用
????@JsonIgnore
????@Override
????public?boolean?isEnabled()?{
????????return?true;
????}
}
復制代碼

我這個其實也很簡單,只是一些用戶名,密碼狀態和權限的集合這些。

編寫一個工具類來生成令牌等…

@Data
@ConfigurationProperties(prefix?=?"jwt")
@Component
public?class?JwtTokenUtil?implements?Serializable?{

????private?String?secret;

????private?Long?expiration;

????private?String?header;

????/**
?????*?從數據聲明生成令牌
?????*
?????*?@param?claims?數據聲明
?????*?@return?令牌
?????*/

????private?String?generateToken(Map<String,?Object>?claims)?{
????????Date?expirationDate?=?new?Date(System.currentTimeMillis()?+?expiration);
????????return?Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512,?secret).compact();
????}

????/**
?????*?從令牌中獲取數據聲明
?????*
?????*?@param?token?令牌
?????*?@return?數據聲明
?????*/

????private?Claims?getClaimsFromToken(String?token)?{
????????Claims?claims;
????????try?{
????????????claims?=?Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
????????}?catch?(Exception?e)?{
????????????claims?=?null;
????????}
????????return?claims;
????}

????/**
?????*?生成令牌
?????*
?????*?@param?userDetails?用戶
?????*?@return?令牌
?????*/

????public?String?generateToken(UserDetails?userDetails)?{
????????Map<String,?Object>?claims?=?new?HashMap<>(2);
????????claims.put("sub",?userDetails.getUsername());
????????claims.put("created",?new?Date());
????????return?generateToken(claims);
????}

????/**
?????*?從令牌中獲取用戶名
?????*
?????*?@param?token?令牌
?????*?@return?用戶名
?????*/

????public?String?getUsernameFromToken(String?token)?{
????????String?username;
????????try?{
????????????Claims?claims?=?getClaimsFromToken(token);
????????????username?=?claims.getSubject();
????????}?catch?(Exception?e)?{
????????????username?=?null;
????????}
????????return?username;
????}

????/**
?????*?判斷令牌是否過期
?????*
?????*?@param?token?令牌
?????*?@return?是否過期
?????*/

????public?Boolean?isTokenExpired(String?token)?{
????????try?{
????????????Claims?claims?=?getClaimsFromToken(token);
????????????Date?expiration?=?claims.getExpiration();
????????????return?expiration.before(new?Date());
????????}?catch?(Exception?e)?{
????????????return?false;
????????}
????}

????/**
?????*?刷新令牌
?????*
?????*?@param?token?原令牌
?????*?@return?新令牌
?????*/

????public?String?refreshToken(String?token)?{
????????String?refreshedToken;
????????try?{
????????????Claims?claims?=?getClaimsFromToken(token);
????????????claims.put("created",?new?Date());
????????????refreshedToken?=?generateToken(claims);
????????}?catch?(Exception?e)?{
????????????refreshedToken?=?null;
????????}
????????return?refreshedToken;
????}

????/**
?????*?驗證令牌
?????*
?????*?@param?token???????令牌
?????*?@param?userDetails?用戶
?????*?@return?是否有效
?????*/

????public?Boolean?validateToken(String?token,?UserDetails?userDetails)?{
????????JwtUser?user?=?(JwtUser)?userDetails;
????????String?username?=?getUsernameFromToken(token);
????????return?(username.equals(user.getUsername())?&&?!isTokenExpired(token));
????}
}
復制代碼

這個類就是一些生成令牌,驗證等等一些操作。具體看注釋~

編寫一個Filter

@Slf4j
@Component
public?class?JwtAuthenticationTokenFilter?extends?OncePerRequestFilter?{



????@Autowired
????private?UserDetailsService?userDetailsService;

????@Autowired
????private?JwtTokenUtil?jwtTokenUtil;


????@Override
????protected?void?doFilterInternal(HttpServletRequest?request,?HttpServletResponse?response,?FilterChain?chain)?throws?ServletException,?IOException?{
????????String?authHeader?=?request.getHeader(jwtTokenUtil.getHeader());
????????if?(authHeader?!=?null?&&?StringUtils.isNotEmpty(authHeader))?{
????????????String?username?=?jwtTokenUtil.getUsernameFromToken(authHeader);
????????????log.info(username);
????????????if?(username?!=?null?&&?SecurityContextHolder.getContext().getAuthentication()?==?null)?{
????????????????UserDetails?userDetails?=?this.userDetailsService.loadUserByUsername(username);
????????????????if?(jwtTokenUtil.validateToken(authHeader,?userDetails))?{
????????????????????UsernamePasswordAuthenticationToken?authentication?=?new?UsernamePasswordAuthenticationToken(userDetails,?null,?userDetails.getAuthorities());
????????????????????authentication.setDetails(new?WebAuthenticationDetailsSource().buildDetails(request));
????????????????????SecurityContextHolder.getContext().setAuthentication(authentication);
????????????????}
????????????}
????????}
????????chain.doFilter(request,?response);
????}
}
復制代碼

這個其實就是用來驗證令牌的是否合法,由于今天這個Demo是一個簡單的登錄返回token的過程,所以這個默認不會去執行里面的邏輯。但是以后登陸后的操作就會執行里面的邏輯了。

JwtUserDetailsServiceImpl

JwtUserDetailsServiceImpl這個實現類是實現了UserDetailsService,UserDetailsService是Spring Security進行身份驗證的時候會使用,我們這里就一個加載用戶信息的簡單方法,就是得到當前登錄用戶的一些用戶名、密碼、用戶所擁有的角色等等一些信息

@Slf4j
@Service
public?class?JwtUserDetailsServiceImpl?implements?UserDetailsService?{



????@Autowired
????private?UserMapper?userMapper;
????@Override
????public?UserDetails?loadUserByUsername(String?s)?throws?UsernameNotFoundException?{

????????User?user?=?userMapper.selectByUserName(s);
????????if(user?==?null){
????????????throw?new?UsernameNotFoundException(String.format("'%s'.這個用戶不存在",?s));
????????}
????????List<SimpleGrantedAuthority>?collect?=?user.getRoles().stream().map(Role::getRolename).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
????????return?new?JwtUser(user.getUsername(),?user.getPassword(),?user.getState(),?collect);
????}
}
復制代碼

編寫登錄的業務實現類

@Slf4j
@Service
public?class?UserServiceImpl?implements?UserService?{
????@Autowired
????private?UserMapper?userMapper;



????@Autowired
????private?AuthenticationManager?authenticationManager;

????@Autowired
????private?UserDetailsService?userDetailsService;

????@Autowired
????private?JwtTokenUtil?jwtTokenUtil;


????public?User?findByUsername(String?username)?{
????????User?user?=?userMapper.selectByUserName(username);
????????log.info("userserviceimpl"+user);
????????return?user;
????}

????public?RetResult?login(String?username,?String?password)?throws?AuthenticationException?{
????????UsernamePasswordAuthenticationToken?upToken?=?new?UsernamePasswordAuthenticationToken(username,?password);
????????final?Authentication?authentication?=?authenticationManager.authenticate(upToken);
????????SecurityContextHolder.getContext().setAuthentication(authentication);
????????UserDetails?userDetails?=?userDetailsService.loadUserByUsername(username);
????????return?new?RetResult(RetCode.SUCCESS.getCode(),jwtTokenUtil.generateToken(userDetails));
????}
}
復制代碼

從上面可以看到login方法,會根據用戶信息然后返回一個token給我們。

WebSecurityConfig

這個就是Spring Security的配置類了

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled?=?true)
public?class?WebSecurity?extends?WebSecurityConfigurerAdapter?{

????@Autowired
????private?UserDetailsService?userDetailsService;

????@Autowired
????private?JwtAuthenticationTokenFilter?jwtAuthenticationTokenFilter;

????@Autowired
????public?void?configureAuthentication(AuthenticationManagerBuilder?authenticationManagerBuilder)throws?Exception{
????????authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder());
????}


????@Bean(name?=?BeanIds.AUTHENTICATION_MANAGER)
????@Override
????public?AuthenticationManager?authenticationManagerBean()?throws?Exception?{
????????return?super.authenticationManagerBean();
????}

????@Bean
????public?PasswordEncoder?passwordEncoder()?{
????????return?new?BCryptPasswordEncoder();
????}

????@Override
????protected?void?configure(HttpSecurity?http)?throws?Exception?{

????????http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
????????????????.and().authorizeRequests()
????????????????.antMatchers(HttpMethod.OPTIONS,?"/**").permitAll()
????????????????.antMatchers("/auth/**").permitAll()
????????????????.anyRequest().authenticated()
????????????????.and().headers().cacheControl();

????????http.addFilterBefore(jwtAuthenticationTokenFilter,?UsernamePasswordAuthenticationFilter.class);


????????ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry?registry?=?http.authorizeRequests();
????????//讓Spring?security放行所有preflight?request
????????registry.requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
????}

????@Bean
????public?CorsFilter?corsFilter()?{
????????final?UrlBasedCorsConfigurationSource?urlBasedCorsConfigurationSource?=?new?UrlBasedCorsConfigurationSource();
????????final?CorsConfiguration?cors?=?new?CorsConfiguration();
????????cors.setAllowCredentials(true);
????????cors.addAllowedOrigin("*");
????????cors.addAllowedHeader("*");
????????cors.addAllowedMethod("*");
????????urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",?cors);
????????return?new?CorsFilter(urlBasedCorsConfigurationSource);
????}
}
復制代碼

我們可以在這里設置自定義的攔截規則,注意在Spring Security5.x中我們要顯式注入AuthenticationManager不然會報錯~

@Bean(name?=?BeanIds.AUTHENTICATION_MANAGER)
????@Override
????public?AuthenticationManager?authenticationManagerBean()?throws?Exception?{
????????return?super.authenticationManagerBean();
????}
復制代碼

目前基本的都已經完成了剩下的就是一些entity,controller的代碼了具體可以看我GitHub上的代碼。
如果有任何見解或者我有寫錯的地方可以聯系我…我一定改…

下載項目到本地,然后改一下mysql的數據庫信息,運行就可以看到返回的token數據了。

歡迎大家關注我的個人公眾號~

轉載于:https://juejin.im/post/5c30c605518825254e4d10f5

總結

以上是生活随笔為你收集整理的Spring Security(一):整合JWT的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。