SpringSecurity和jwt實現(xiàn)登錄及權(quán)限認證功能
系列文章目錄
spring security+jwt安全方案
前言
前面我們已經(jīng)通過使用springboot框架獲得了管理數(shù)據(jù)的基本能力,但是一個系統(tǒng)不和或缺的功能是安全登錄。
這里我們以springsecurity+jwt方案實現(xiàn)登錄以及權(quán)限控制。
一、springsecurity+jwt方案
提示:這里是對該方案的原理簡介
一個安全的系統(tǒng)是需要對請求身份進行認證的。
但是http協(xié)議是無狀態(tài)的,所以需要對每次的請求進行校驗。
以下是jwt方案流程圖,我們以現(xiàn)實生活為例。當我們被一個學校錄取,我們在開學的時候需要提供身份證(類比賬號密碼),學校就會發(fā)放一個學生證(類比jwt令牌),這樣我們每次進學校帶學生證就行了(每次使用系統(tǒng)帶jwt就行了)

二、權(quán)限控制RBAC
提示:這里是對登錄的細化,即權(quán)限功能
登錄系統(tǒng)的人并不只是一個人,以下為RBAC的數(shù)據(jù)庫設(shè)計圖。
我們依然以現(xiàn)實世界為例,一個教務系統(tǒng),有很多用戶(user);其中有兩種身份(role):老師和學生;老師和學生擁有不一樣的功能(menu),老師可以改卷子打分等等。

三、實現(xiàn)
我們以該圖為例,該流程即為需要實現(xiàn)的。

1.RBAC數(shù)據(jù)庫實現(xiàn)

這里請自行搜索RBAC的sql代碼
2.攔截器實現(xiàn)

給系統(tǒng)套上一層攔截功能即是security實現(xiàn)的功能,這里先實現(xiàn)接口放行
在maven添加以下依賴后:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>系統(tǒng)會自動生成一個登錄頁面,我們需要做的就是給登錄接口放行,其他接口攔截的配置
參考以下配置
package com.nie.sportserver.config;
import com.nie.sportserver.Interceptor.JwtTokenAdminInterceptor;
import com.nie.sportserver.exception.MyAccessDeniedHandler;
import com.nie.sportserver.exception.MyAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.time.Duration;
import java.util.Arrays;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
MyAccessDeniedHandler myAccessDeniedHandler;
@Autowired
MyAuthenticationEntryPoint myAuthenticationEntryPoint;
@Autowired
JwtTokenAdminInterceptor jwtTokenAdminInterceptor;
//加密算法
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//security配置跨域
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOriginPattern("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(source);
}
//配置安全攔截
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()//關(guān)閉csrf
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//不通過Session獲取Securitycontext
.and()//配置異常處理
.exceptionHandling()
.authenticationEntryPoint(myAuthenticationEntryPoint)
.accessDeniedHandler(myAccessDeniedHandler)
.and()
.authorizeRequests()
//接口匿名訪問
.antMatchers("/doc.html",
"/favicon.ico",
"/v2/api-docs",
"/swagger-resources/**",
"/webjars/**","/user/login").anonymous()//攜帶token了就無法訪問了
.anyRequest().authenticated();
http.addFilterBefore(jwtTokenAdminInterceptor, UsernamePasswordAuthenticationFilter.class);
}
//暴露認證方法變?yōu)閎ean對象
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}關(guān)鍵在于以下代碼
.antMatchers("/doc.html",
"/favicon.ico",
"/v2/api-docs",
"/swagger-resources/**",
"/webjars/**","/user/login").anonymous()3.登錄接口實現(xiàn)

由于之前已經(jīng)實現(xiàn)了放行,我們只需要完成查詢數(shù)據(jù)庫,并且將數(shù)據(jù)生成jwt即可
在上面的配置中,我們已經(jīng)把認證方法暴露為bean對象,我們實現(xiàn)該方法即可
//暴露認證方法變?yōu)閎ean對象
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
示例如下
package com.nie.sportserver.service.impl;
import com.nie.sportpojo.entity.LoginUser;
import com.nie.sportpojo.entity.User;
import com.nie.sportserver.mapper.LoginMapper;
import com.nie.sportserver.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private LoginMapper loginMapper;
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//查詢用戶信息
User user = loginMapper.getByUserName(username);
//把數(shù)據(jù)封裝為UserDetail返回
//todo 查詢對應的權(quán)限信息
List<String> list = new ArrayList<>(userMapper.selectPermsByUserId(user.getId()));
LoginUser loginUser = new LoginUser(user,list);
return loginUser;
}
}4.攔截器實現(xiàn)

在前面的配置中,我們已經(jīng)將普通請求攔截了,并且使用攔截器
@Autowired
JwtTokenAdminInterceptor jwtTokenAdminInterceptor;
這里來實現(xiàn)攔截器
package com.nie.sportserver.Interceptor;
import com.nie.sportcommon.utills.JwtUtil;
import com.nie.sportpojo.entity.LoginUser;
import com.nie.sportpojo.entity.User;
import com.nie.sportserver.mapper.LoginMapper;
import com.nie.sportserver.mapper.UserMapper;
import com.nie.sportserver.properties.JwtProperties;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Component
@Slf4j
public class JwtTokenAdminInterceptor extends OncePerRequestFilter {
@Autowired
private JwtProperties jwtProperties;
@Autowired
private LoginMapper loginMapper;
@Autowired
private UserMapper userMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String requestURI = request.getRequestURI();
//從請求頭中獲取令牌
String token = request.getHeader(jwtProperties.getAdminTokenName());
if (!StringUtils.hasText((token))) {
filterChain.doFilter(request, response);
return;
}
//校驗令牌
Long userId;
try {
log.info("jwt校驗{}", token);
//token解析
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
userId = Long.valueOf(claims.get("userId").toString());
log.info("當前用戶id:{}", userId);
} catch (Exception ex) {
throw new RuntimeException("token非法");
}
User user = loginMapper.getByUserId(userId);
List<String> list = new ArrayList<>(userMapper.selectPermsByUserId(user.getId()));
LoginUser loginUser = new LoginUser(user,list);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
//放行
filterChain.doFilter(request, response);
}
}其他工具類
jwt依賴
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>jwt工具類
package com.nie.sportcommon.utills;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
public class JwtUtil {
/**
* 生成jwt
* 使用Hs256算法, 私匙使用固定秘鑰
*
* @param secretKey jwt秘鑰
* @param ttlMillis jwt過期時間(毫秒)
* @param claims 設(shè)置的信息
* @return
*/
public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
// 指定簽名的時候使用的簽名算法,也就是header那部分
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 生成JWT的時間
long expMillis = System.currentTimeMillis() + ttlMillis;
Date exp = new Date(expMillis);
// 設(shè)置jwt的body
JwtBuilder builder = Jwts.builder()
// 如果有私有聲明,一定要先設(shè)置這個自己創(chuàng)建的私有的聲明,這個是給builder的claim賦值,一旦寫在標準的聲明賦值之后,就是覆蓋了那些標準的聲明的
.setClaims(claims)
// 設(shè)置簽名使用的簽名算法和簽名使用的秘鑰
.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
// 設(shè)置過期時間
.setExpiration(exp);
return builder.compact();
}
/**
* Token解密
*
* @param secretKey jwt秘鑰 此秘鑰一定要保留好在服務端, 不能暴露出去, 否則sign就可以被偽造, 如果對接多個客戶端建議改造成多個
* @param token 加密后的token
* @return
*/
public static Claims parseJWT(String secretKey, String token) {
// 得到DefaultJwtParser
Claims claims = Jwts.parser()
// 設(shè)置簽名的秘鑰
.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
// 設(shè)置需要解析的jwt
.parseClaimsJws(token).getBody();
return claims;
}
}權(quán)限
權(quán)限認證使用 @PreAuthorize(“hasAuthority(‘’)”)注解
總結(jié)
本文對jwt登錄校驗,權(quán)限管理的原理簡單描述,并且提供了實現(xiàn)方案
到此這篇關(guān)于SpringSecurity+jwt實現(xiàn)登錄及權(quán)限認證功能的文章就介紹到這了,更多相關(guān)SpringSecurity jwt權(quán)限認證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud使用集中配置組件Config規(guī)避信息泄露
項目應用中,數(shù)據(jù)庫連接信息、Access-key、Secret-key等由于其及其敏感和特殊性,一旦泄露出去就很可能會使得應用遭到黑客攻擊,例如數(shù)據(jù)庫賬號密碼泄露可能導致“拖庫”,甚至數(shù)據(jù)丟失。此等事件偶有發(fā)生,那么,在分布式微服務項目中,怎么避免這種情況呢2022-07-07
Dubbo?LoadBalance基于權(quán)重的隨機負載均衡算法提高服務性能
這篇文章主要為大家介紹了Dubbo?LoadBalance基于權(quán)重的隨機負載均衡算法提高服務性能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪<BR>2023-10-10
Maven的配置文件pom.xml詳解(含常用plugin)
pom.xml是Maven項目的核心配置文件,它是 項目對象模型 - Project Object Model(POM)的縮寫,本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來管理項目,感興趣的朋友跟隨小編一起看看吧2024-08-08
Java數(shù)據(jù)結(jié)構(gòu)之常見排序算法(下)
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之常見排序算法(下),與之相對有(上),想了解的朋友可以去本網(wǎng)站掃搜,在這兩篇文章里涵蓋關(guān)于八大排序算法的所有內(nèi)容,需要的朋友可以參考下2023-01-01
SpringBoot?+?Redis如何解決重復提交問題(冪等)
在開發(fā)中,一個對外暴露的接口可能會面臨瞬間的大量重復請求,本文就介紹了SpringBoot + Redis如何解決重復提交問題,具有一定的參考價值,感興趣的可以了解一下2021-12-12

