SpringBoot+SpringSecurity+jwt實(shí)現(xiàn)驗(yàn)證
記錄一下使用springSecurity實(shí)現(xiàn)jwt的授權(quán)方法,這方法可以實(shí)現(xiàn)權(quán)限的基本認(rèn)證。當(dāng)然這個(gè)案例還有許多的問題,不過還是先記錄一下。其他功能以后在補(bǔ)充。
建議工程創(chuàng)建流程
- 創(chuàng)建 JwtTokenUtils
- 創(chuàng)建 jwtAccessDeniedHandler 和 JwtAuthenticationEntryPoint
- 創(chuàng)建 UserDetailsServiceImpl
- 創(chuàng)建 JwtAuthenticationFilter
- 配置 Security信息
- 啟動類的信息
環(huán)境
- springBoot 2.3.3
- springSecurity 5.0
- jjwt 0.91
pox.xml 文件主要信息
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</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>
目錄結(jié)構(gòu)信息
請忽略文件命名

jwtAccessDeniedHandler 和 JwtAuthenticationEntryPoint
這兩個(gè)類的作用是用戶訪問沒有授權(quán)資源和攜帶錯誤token的錯誤返回處理信息類,要使用這兩個(gè)類只需要在security的配置文件中配置一下就可以只用了
/**
* @author Bxsheng
* @blogAddress www.kdream.cn
* @createTIme 2020/9/17
* since JDK 1.8
* 當(dāng)用戶在沒有授權(quán)的時(shí)候,返回的指定信息
*/
@Component
public class jwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
System.out.println("用戶訪問沒有授權(quán)資源");
System.out.println(e.getMessage());
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e==null?"用戶訪問沒有授權(quán)資源":e.getMessage());
}
}
/**
* @author Bxsheng
* @blogAddress www.kdream.cn
* @createTIme 2020/9/17
* since JDK 1.8
*/
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
System.out.println("用戶訪問資源沒有攜帶正確的token");
System.out.println(e.getMessage());
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e==null?"用戶訪問資源沒有攜帶正確的token":e.getMessage());
}
}
UserDetailsServiceImpl 登錄信息驗(yàn)證
該類直接繼承UserDetailsService 進(jìn)行登錄信息驗(yàn)證,在輸入賬戶密碼進(jìn)行登錄的時(shí)候,會進(jìn)入這個(gè)類進(jìn)行驗(yàn)證信息。
當(dāng)然我這里是直接使用了寫死的密碼,正常應(yīng)該從數(shù)據(jù)庫中獲取用戶的信息和權(quán)限信息
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//直接寫死數(shù)據(jù)信息,可以在這里獲取數(shù)據(jù)庫的信息并進(jìn)行驗(yàn)證
UserDetails userDetails = User.withUsername(s).password(new BCryptPasswordEncoder().encode("123456"))
.authorities("bxsheng").build();
return userDetails;
}
}
JwtTokenUtils jwt包裝類
該類直接使用 slyh 的 [SpringBoot+JWT實(shí)現(xiàn)登錄權(quán)限控制(代碼))](( http://www.dbjr.com.cn/article/257119.htm)的文章里面的類。
package cn.kdream.securityjwt.utlis;
import io.jsonwebtoken.*;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author Bxsheng
* @blogAddress www.kdream.cn
* @createTIme 2020/9/16
* since JDK 1.8
*/
public class JwtTokenUtils {
public static final String TOKEN_HEADER = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
public static final String SECRET = "jwtsecret";
public static final String ISS = "echisan";
private static final Long EXPIRATION = 60 * 60 * 3L; //過期時(shí)間3小時(shí)
private static final String ROLE = "role";
//創(chuàng)建token
public static String createToken(String username, String role, boolean isRememberMe){
Map map = new HashMap();
map.put(ROLE, role);
return Jwts.builder()
.signWith(SignatureAlgorithm.HS512, SECRET)
.setClaims(map)
.setIssuer(ISS)
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000))
.compact();
}
//從token中獲取用戶名(此處的token是指去掉前綴之后的)
public static String getUserName(String token){
String username;
try {
username = getTokenBody(token).getSubject();
} catch ( Exception e){
username = null;
}
return username;
}
public static String getUserRole(String token){
return (String) getTokenBody(token).get(ROLE);
}
private static Claims getTokenBody(String token){
Claims claims = null;
try{
claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
} catch(ExpiredJwtException e){
e.printStackTrace();
} catch(UnsupportedJwtException e){
e.printStackTrace();
} catch(MalformedJwtException e){
e.printStackTrace();
} catch(SignatureException e){
e.printStackTrace();
} catch(IllegalArgumentException e){
e.printStackTrace();
}
return claims;
}
//是否已過期
public static boolean isExpiration(String token){
try{
return getTokenBody(token).getExpiration().before(new Date());
} catch(Exception e){
System.out.println(e.getMessage());
}
return true;
}
}
JwtAuthenticationFilter 自定義驗(yàn)證jwt
該類直接使用 slyh 的 [SpringBoot+JWT實(shí)現(xiàn)登錄權(quán)限控制(代碼))](( http://www.dbjr.com.cn/article/257119.htm))的文章里面的類。
這個(gè)類主要的作用是驗(yàn)證jwt信息 ,主要攜帶了token請求過來,解析jwt并設(shè)置在security的上下文中。這樣做的其中一個(gè)目的是你獲得了token中攜帶的權(quán)限信息,并保存在上下文中。你就可以對用戶進(jìn)行權(quán)限認(rèn)證了
/**
* @author Bxsheng
* @blogAddress www.kdream.cn
* @createTIme 2020/9/16
* since JDK 1.8
*/
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER);
//如果請求頭中沒有Authorization信息則直接放行了
if(tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)){
chain.doFilter(request, response);
return;
}
//如果請求頭中有token,則進(jìn)行解析,并且設(shè)置認(rèn)證信息
if(!JwtTokenUtils.isExpiration(tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX,""))){
//設(shè)置上下文
UsernamePasswordAuthenticationToken authentication = getAuthentication(tokenHeader);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
super.doFilterInternal(request, response, chain);
}
//獲取用戶信息
private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader){
String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, "");
String username = JwtTokenUtils.getUserName(token);
// 獲得權(quán)限 添加到權(quán)限上去
String role = JwtTokenUtils.getUserRole(token);
List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();
roles.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return role;
}
});
if(username != null){
return new UsernamePasswordAuthenticationToken(username, null,roles);
}
return null;
}
}
security的配置信息
@EnableGlobalMethodSecurity(prePostEnabled = true) 開啟prePostEnabled注解方式授權(quán)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityJwtConfig extends WebSecurityConfigurerAdapter {
@Autowired
private jwtAccessDeniedHandler jwtAccessDeniedHandler;
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**")
.permitAll()
.antMatchers("/").permitAll()
//login 不攔截
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
//授權(quán)
.and()
// 禁用session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 使用自己定義的攔截機(jī)制,攔截jwt
http.addFilterBefore(new JwtAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class)
//授權(quán)錯誤信息處理
.exceptionHandling()
//用戶訪問資源沒有攜帶正確的token
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
//用戶訪問沒有授權(quán)資源
.accessDeniedHandler(jwtAccessDeniedHandler);
}
@Bean
public PasswordEncoder passwordEncoder(){
//使用的密碼比較方式
return new BCryptPasswordEncoder();
}
}
啟動類
我在啟動類中配置了三個(gè)方法,一個(gè)是用來進(jìn)行登錄信息的,另外兩個(gè)設(shè)置了需要權(quán)限訪問
@SpringBootApplication
@RestController
public class SecurityJwtApplication {
private final AuthenticationManagerBuilder authenticationManagerBuilder;
public SecurityJwtApplication(AuthenticationManagerBuilder authenticationManagerBuilder) {
this.authenticationManagerBuilder = authenticationManagerBuilder;
}
public static void main(String[] args) {
SpringApplication.run(SecurityJwtApplication.class, args);
}
@GetMapping("/")
public String index(){
return "security jwt";
}
@PostMapping("/login")
public String login(@RequestParam String u,@RequestParam String p){
// 登陸驗(yàn)證
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(u, p);
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
//創(chuàng)建jwt信息
String token1 = JwtTokenUtils.createToken(u,"bxsheng", true);
return token1;
}
@GetMapping("/role")
@PreAuthorize("hasAnyAuthority('bxsheng')")
public String roleInfo(){
return "需要獲得bxsheng權(quán)限,才可以訪問";
}
@GetMapping("/roles")
@PreAuthorize("hasAnyAuthority('kdream')")
public String rolekdream(){
return "需要獲得kdream權(quán)限,才可以訪問";
}
}
效果
直接訪問需要授權(quán)的用戶信息
直接沒有使用token直接訪問只要授權(quán)的資源信息,會進(jìn)入JwtAuthenticationEntryPoint 類

獲取token
訪問在啟動類中的login方法,獲取token信息
因?yàn)槲沂褂昧斯潭ǖ拿艽a,所以在使用錯誤的密碼訪問的時(shí)候,可以在springboot的全局異常處理中捕獲到異常信息
/**
* @author Bxsheng
* @blogAddress www.kdream.cn
* @createTIme 2020/9/17
* since JDK 1.8
*/
@RestControllerAdvice
public class Error {
@ExceptionHandler(BadCredentialsException.class)
public void badCredentialsException(BadCredentialsException e){
System.out.println(e.getMessage());//用戶名或密碼錯誤
// throw new BadCredentialsException(e.getMessage());
}
}

正確的獲取token,并進(jìn)行受保護(hù)的資源訪問
里面有寫死的bxsheng權(quán)限信息,所以正常是可以獲取bxsheng標(biāo)識的資源信息的。

成功獲取信息

嘗試獲取無權(quán)限資源信息
使用token直接訪問無權(quán)限資源信息,會進(jìn)入jwtAccessDeniedHandler 類

至此應(yīng)該可以說算完成簡單的權(quán)限管理了。
到此這篇關(guān)于SpringBoot+SpringSecurity+jwt實(shí)現(xiàn)驗(yàn)證的文章就介紹到這了,更多相關(guān)SpringBoot SpringSecurity jwt 驗(yàn)證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot整合SpringSecurity和JWT的示例
- SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實(shí)現(xiàn)
- SpringBoot3.0+SpringSecurity6.0+JWT的實(shí)現(xiàn)
- Springboot WebFlux集成Spring Security實(shí)現(xiàn)JWT認(rèn)證的示例
- 詳解SpringBoot+SpringSecurity+jwt整合及初體驗(yàn)
- SpringBoot集成Spring security JWT實(shí)現(xiàn)接口權(quán)限認(rèn)證
- SpringBoot3.x接入Security6.x實(shí)現(xiàn)JWT認(rèn)證的完整步驟
- SpringBoot Security+JWT簡單搭建的實(shí)現(xiàn)示例
相關(guān)文章
Spring?IOC?xml方式進(jìn)行工廠Bean操作詳解
這篇文章主要介紹了Spring?IOC?xml方式進(jìn)行工廠Bean操作,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01
SpringBoot整合FastDFS中間件實(shí)現(xiàn)文件分布管理
FastDFS是一個(gè)開源的輕量級分布式文件系統(tǒng),它對文件進(jìn)行管理,功能包括:文件存儲、文件同步、文件上傳、文件下載等,解決了大容量存儲和負(fù)載均衡的問題,本文介紹了SpringBoot整合FastDFS中間件實(shí)現(xiàn)文件分布管理,需要的朋友可以參考下2024-08-08
SpringBoot實(shí)現(xiàn)JWT 認(rèn)證的項(xiàng)目實(shí)踐
本文介紹了Spring Boot中實(shí)現(xiàn)JWT認(rèn)證,并介紹了擴(kuò)展JWT認(rèn)證功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-08-08
利用Java實(shí)現(xiàn)讀寫bmp文件的示例代碼
位圖(Bitmap,BMP)是一種最原始、最簡單的圖像文件格式,由微軟和 IBM 在 1980 年代聯(lián)合制定,用于 Windows 操作系統(tǒng),本項(xiàng)目旨在用純 Java 從零實(shí)現(xiàn)一個(gè)輕量級的 BMP 文件讀寫庫,需要的朋友可以參考下2025-05-05
java使用BeanUtils.copyProperties方法對象復(fù)制同名字段類型不同賦值為空問題解決方案
這篇文章主要給大家介紹了關(guān)于java使用BeanUtils.copyProperties方法對象復(fù)制同名字段類型不同賦值為空問題的解決方案,文中通過代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-11-11

