Spring Security + jwt 許可權系統設計,包含SQL
完整專案程式碼
https://download.csdn.net/download/y1534414425/13123475
引言
這是一個Spring Security + jwt 做的一個許可權系統設計的demo,註冊預設是使用者角色,沒有登入的情況下不可以訪問使用者和管理員介面,每個角色擁有訪問指定路徑下的介面,管理員許可權繼承自使用者,所以管理員擁有使用者的所有許可權,使用者訪問不了管理員介面,。
一、資料設計
二、關鍵部分程式碼
Spring Security主要配置SecurityConfig
package com.springsecurity.security.config;
import com.springsecurity.security.exception.JwtAccessDeniedHandler;
import com.springsecurity.security.exception.JwtAuthenticationEntryPoint;
import com.springsecurity.security.filter.JwtAuthenticationFilter;
import com.springsecurity.security.filter.JwtAuthorizationFilter;
import com.springsecurity.security.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
/**
* 密碼編碼器
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 設定自定義的userDetailsService以及密碼編碼器
auth.userDetailsService(userDetailsServiceImpl)
// 配置密碼加密規則
.passwordEncoder(bCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
// 禁用 CSRF
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/auth/login").permitAll()
// 指定路徑下的資源需要管理員許可權才能訪問
.antMatchers("/user/admin/**").hasRole("ADMIN")
// 指定路徑下的資源需要使用者許可權才能訪問
.antMatchers("/user/*").hasRole("USER")
// 其他都放行了
.anyRequest().permitAll()
.and()
//新增自定義Filter
.addFilter(new JwtAuthenticationFilter(authenticationManager())) // 認證
.addFilter(new JwtAuthorizationFilter(authenticationManager(), userDetailsServiceImpl)) // 授權
// 不需要session(不建立會話)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// 授權異常處理
.exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint())
.accessDeniedHandler(new JwtAccessDeniedHandler())
.and()
.formLogin().loginProcessingUrl("/login").permitAll();
// 防止 web 頁面的Frame 被攔截
http.headers().frameOptions().disable();
}
/**
* 角色繼承
*
* @return
*/
@Bean
RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
String hierarchy = "ROLE_ADMIN > ROLE_USER";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
}
認證過濾器
package com.springsecurity.security.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springsecurity.exception.LoginFailedException;
import com.springsecurity.security.constants.SecurityConstants;
import com.springsecurity.security.dto.LoginRequest;
import com.springsecurity.security.entity.JwtUser;
import com.springsecurity.security.utils.JwtTokenUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
/**
* 如果使用者名稱和密碼正確,那麼過濾器將建立一個JWT Token 並在HTTP Response 的header中返回它,格式:token: "Bearer +具體token值"
*/
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final ThreadLocal<Boolean> rememberMe = new ThreadLocal<>();
private final AuthenticationManager authenticationManager;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
// 設定URL,以確定是否需要身份驗證
super.setFilterProcessesUrl(SecurityConstants.AUTH_LOGIN_URL);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
ObjectMapper objectMapper = new ObjectMapper();
try {
// 獲取登入的資訊
LoginRequest loginRequest = objectMapper.readValue(request.getInputStream(), LoginRequest.class);
rememberMe.set(loginRequest.getRememberMe());
// 這部分和attemptAuthentication方法中的原始碼是一樣的,
// 只不過由於這個方法原始碼的是把使用者名稱和密碼這些引數的名字是死的,所以我們重寫了一下
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(), loginRequest.getPassword());
return authenticationManager.authenticate(authentication);
} catch (IOException | AuthenticationException e) {
if (e instanceof AuthenticationException) {
throw new LoginFailedException("登入失敗!請檢查使用者名稱和密碼。");
}
throw new LoginFailedException(e.getMessage());
}
}
/**
* 如果驗證成功,就生成token並返回
*/
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authentication) {
JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
List<String> authorities = jwtUser.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
// 建立 Token
String token = JwtTokenUtils.createToken(jwtUser.getUsername(), authorities, rememberMe.get());
rememberMe.remove();
// Http Response Header 中返回 Token
response.setHeader(SecurityConstants.TOKEN_HEADER, token);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage());
}
}
授權過濾器
package com.springsecurity.security.filter;
import com.springsecurity.exception.UserNameNotFoundException;
import com.springsecurity.security.constants.SecurityConstants;
import com.springsecurity.security.service.UserDetailsServiceImpl;
import com.springsecurity.security.utils.JwtTokenUtils;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.security.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
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.logging.Logger;
/**
* 過濾器處理所有HTTP請求,並檢查是否存在帶有正確令牌的Authorization標頭。例如,如果令牌未過期或簽名金鑰正確。
*/
@Slf4j
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private final UserDetailsServiceImpl userDetailsService;
private static final Logger logger = Logger.getLogger(JwtAuthorizationFilter.class.getName());
public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserDetailsServiceImpl userDetailsService) {
super(authenticationManager);
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
String token = request.getHeader(SecurityConstants.TOKEN_HEADER);
if (token == null || !token.startsWith(SecurityConstants.TOKEN_PREFIX)) {
SecurityContextHolder.clearContext();
} else {
UsernamePasswordAuthenticationToken authentication = getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
/**
* 獲取使用者認證資訊 Authentication
*/
private UsernamePasswordAuthenticationToken getAuthentication(String authorization) {
log.info("get authentication");
String token = authorization.replace(SecurityConstants.TOKEN_PREFIX, "");
try {
String username = JwtTokenUtils.getUsernameByToken(token);
logger.info("checking username:" + username);
if (!StringUtils.isEmpty(username)) {
// 這裡我們是又從資料庫拿了一遍,避免使用者的角色資訊有變
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, null, userDetails.getAuthorities());
return userDetails.isEnabled() ? usernamePasswordAuthenticationToken : null;
}
} catch (UserNameNotFoundException | SignatureException | ExpiredJwtException | MalformedJwtException | IllegalArgumentException exception) {
logger.warning("Request to parse JWT with invalid signature . Detail : " + exception.getMessage());
}
return null;
}
}
三、效果
使用者登入
未登入訪問使用者介面
使用者訪問使用者介面
使用者訪問管理員介面
管理員登入
未登入訪問管理員介面
管理員訪問使用者介面
管理員訪問管理員介面
相關文章
- 基於Spring Security和 JWT的許可權系統設計SpringJWT
- SpringBoot整合Spring security JWT實現介面許可權認證Spring BootJWT
- 基於Spring Security實現許可權管理系統Spring
- spring security許可權認證Spring
- Spring security(五)-完美許可權管理系統(授權過程分析)Spring
- 許可權系統:許可權應用服務設計
- 許可權系統:6個許可權概念模型設計模型
- 許可權系統:許可權應用服務設計Tu
- Spring Security實現統一登入與許可權控制Spring
- 基於 Spring Security 的前後端分離的許可權控制系統Spring後端
- Spring Security 基於URL的許可權判斷Spring
- Security 10:許可權管理
- SpringSecurity許可權管理系統實戰—六、SpringSecurity整合JWTSpringGseJWT
- 基於Spring Security Oauth2的SSO單點登入+JWT許可權控制實踐SpringOAuthJWT
- 許可權設計
- 分散式系統中,許可權設計實踐分散式
- 關於系統許可權的設計-位操作
- spring-boot-plus整合Shiro+JWT許可權管理SpringbootJWT
- 專欄丨Spring Security系列教程之Spring Security的四種許可權控制方式Spring
- Linux系統程式設計(七)檔案許可權系統呼叫Linux程式設計
- RBAC許可權---SpringBoot整合SecuritySpring Boot
- 如何利用 Spring Hibernate 高階特性設計實現一個許可權系統Spring
- Spring Security + JWTSpringJWT
- 許可權系統:一文搞懂功能許可權、資料許可權
- learun通用許可權系統框架功能實現設計框架
- 手把手擼套框架-許可權系統設計框架
- 管理系統之許可權的設計和實現
- 通用許可權系統之資料庫表設計資料庫
- sql許可權管理SQL
- Security8:許可權模擬
- 如何設計應用系統的資料許可權管理
- .Net Core JWT 動態設定介面與許可權JWT
- 基於RBAC的許可權控制淺析(結合Spring Security)Spring
- Spring Security 許可權管理的投票器與表決機制Spring
- 有贊許可權系統
- mongodb 的許可權系統MongoDB
- JavaWeb許可權設計原理JavaWeb
- android 許可權元件設計Android元件