您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“怎么使用SpringBoot+SpringSecurity+jwt實現(xiàn)驗證”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“怎么使用SpringBoot+SpringSecurity+jwt實現(xià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>
請忽略文件命名
jwtAccessDeniedHandler 和 JwtAuthenticationEntryPoint
這兩個類的作用是用戶訪問沒有授權(quán)資源和攜帶錯誤token的錯誤返回處理信息類,要使用這兩個類只需要在security的配置文件中配置一下就可以只用了
/** * @author Bxsheng * @blogAddress www.kdream.cn * @createTIme 2020/9/17 * since JDK 1.8 * 當(dāng)用戶在沒有授權(quán)的時候,返回的指定信息 */ @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 登錄信息驗證
該類直接繼承UserDetailsService 進行登錄信息驗證,在輸入賬戶密碼進行登錄的時候,會進入這個類進行驗證信息。
當(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ù)庫的信息并進行驗證 UserDetails userDetails = User.withUsername(s).password(new BCryptPasswordEncoder().encode("123456")) .authorities("bxsheng").build(); return userDetails; } }
JwtTokenUtils jwt包裝類
該類直接使用 slyh 的 [SpringBoot+JWT實現(xiàn)登錄權(quán)限控制(代碼))](( http://kemok4.com/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; //過期時間3小時 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 自定義驗證jwt
該類直接使用 slyh 的 [SpringBoot+JWT實現(xiàn)登錄權(quán)限控制(代碼))](( http://kemok4.com/article/257119.htm))的文章里面的類。
這個類主要的作用是驗證jwt信息 ,主要攜帶了token請求過來,解析jwt并設(shè)置在security的上下文中。這樣做的其中一個目的是你獲得了token中攜帶的權(quán)限信息,并保存在上下文中。你就可以對用戶進行權(quá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,則進行解析,并且設(shè)置認證信息 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); // 使用自己定義的攔截機制,攔截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(); } }
啟動類
我在啟動類中配置了三個方法,一個是用來進行登錄信息的,另外兩個設(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){ // 登陸驗證 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)的資源信息,會進入JwtAuthenticationEntryPoint 類
獲取token
訪問在啟動類中的login方法,獲取token信息
因為我使用了固定的密碼,所以在使用錯誤的密碼訪問的時候,可以在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,并進行受保護的資源訪問
里面有寫死的bxsheng權(quán)限信息,所以正常是可以獲取bxsheng標(biāo)識的資源信息的。
成功獲取信息
嘗試獲取無權(quán)限資源信息
使用token直接訪問無權(quán)限資源信息,會進入jwtAccessDeniedHandler 類
到此,相信大家對“怎么使用SpringBoot+SpringSecurity+jwt實現(xiàn)驗證”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。