摘要:構造函數的第一個參數是對象,所以可以自定義緩存對象。在微服務各個模塊獲取用戶的這些信息的方法如下略權限控制啟用基于方法的權限注解簡單權限校驗例如,刪除角色的接口,僅允許擁有權限的用戶訪問。
微服務架構
網關:路由用戶請求到指定服務,轉發前端 Cookie 中包含的 Session 信息;
用戶服務:用戶登錄認證(Authentication),用戶授權(Authority),用戶管理(Redis Session Management)
其他服務:依賴 Redis 中用戶信息進行接口請求驗證
用戶 - 角色 - 權限表結構設計權限表
權限表最小粒度的控制單個功能,例如用戶管理、資源管理,表結構示例:
id | authority | description |
---|---|---|
1 | ROLE_ADMIN_USER | 管理所有用戶 |
2 | ROLE_ADMIN_RESOURCE | 管理所有資源 |
3 | ROLE_A_1 | 訪問 ServiceA 的某接口的權限 |
4 | ROLE_A_2 | 訪問 ServiceA 的另一個接口的權限 |
5 | ROLE_B_1 | 訪問 ServiceB 的某接口的權限 |
6 | ROLE_B_2 | 訪問 ServiceB 的另一個接口的權限 |
角色 - 權限表
自定義角色,組合各種權限,例如超級管理員擁有所有權限,表結構示例:
id | name | authority_ids |
---|---|---|
1 | 超級管理員 | 1,2,3,4,5,6 |
2 | 管理員A | 3,4 |
3 | 管理員B | 5,6 |
4 | 普通用戶 | NULL |
用戶 - 角色表
用戶綁定一個或多個角色,即分配各種權限,示例表結構:
user_id | role_id |
---|---|
1 | 1 |
1 | 4 |
2 | 2 |
Maven 依賴(所有服務)
org.springframework.boot spring-boot-starter-security org.springframework.session spring-session-data-redis
應用配置 application.yml 示例:
# Spring Session 配置 spring.session.store-type=redis server.servlet.session.persistent=true server.servlet.session.timeout=7d server.servlet.session.cookie.max-age=7d # Redis 配置 spring.redis.host=spring.redis.port=6379 # MySQL 配置 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql:// :3306/test spring.datasource.username= spring.datasource.password=
用戶登錄認證(authentication)與授權(authority)
@Slf4j public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter { private final UserService userService; CustomAuthenticationFilter(String defaultFilterProcessesUrl, UserService userService) { super(new AntPathRequestMatcher(defaultFilterProcessesUrl, HttpMethod.POST.name())); this.userService = userService; } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { JSONObject requestBody = getRequestBody(request); String username = requestBody.getString("username"); String password = requestBody.getString("password"); UserDO user = userService.getByUsername(username); if (user != null && validateUsernameAndPassword(username, password, user)){ // 查詢用戶的 authority ListuserAuthorities = userService.getSimpleGrantedAuthority(user.getId()); return new UsernamePasswordAuthenticationToken(user.getId(), null, userAuthorities); } throw new AuthenticationServiceException("登錄失敗"); } /** * 獲取請求體 */ private JSONObject getRequestBody(HttpServletRequest request) throws AuthenticationException{ try { StringBuilder stringBuilder = new StringBuilder(); InputStream inputStream = request.getInputStream(); byte[] bs = new byte[StreamUtils.BUFFER_SIZE]; int len; while ((len = inputStream.read(bs)) != -1) { stringBuilder.append(new String(bs, 0, len)); } return JSON.parseObject(stringBuilder.toString()); } catch (IOException e) { log.error("get request body error."); } throw new AuthenticationServiceException(HttpRequestStatusEnum.INVALID_REQUEST.getMessage()); } /** * 校驗用戶名和密碼 */ private boolean validateUsernameAndPassword(String username, String password, UserDO user) throws AuthenticationException { return username == user.getUsername() && password == user.getPassword(); } }
@EnableWebSecurity @AllArgsConstructor public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private static final String LOGIN_URL = "/user/login"; private static final String LOGOUT_URL = "/user/logout"; private final UserService userService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(LOGIN_URL).permitAll() .anyRequest().authenticated() .and() .logout().logoutUrl(LOGOUT_URL).clearAuthentication(true).permitAll() .and() .csrf().disable(); http.addFilterAt(bipAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) .rememberMe().alwaysRemember(true); } /** * 自定義認證過濾器 */ private CustomAuthenticationFilter customAuthenticationFilter() { CustomAuthenticationFilter authenticationFilter = new CustomAuthenticationFilter(LOGIN_URL, userService); return authenticationFilter; } }其他服務設計
應用配置 application.yml 示例:
# Spring Session 配置 spring.session.store-type=redis # Redis 配置 spring.redis.host=spring.redis.port=6379
全局安全配置
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .csrf().disable(); } }用戶認證信息獲取
用戶通過用戶服務登錄成功后,用戶信息會被緩存到 Redis,緩存的信息與 CustomAuthenticationFilter 中 attemptAuthentication() 方法返回的對象有關,如上所以,返回的對象是 new UsernamePasswordAuthenticationToken(user.getId(), null, userAuthorities),即 Redis 緩存了用戶的 ID 和用戶的權力(authorities)。
UsernamePasswordAuthenticationToken 構造函數的第一個參數是 Object 對象,所以可以自定義緩存對象。
在微服務各個模塊獲取用戶的這些信息的方法如下:
@GetMapping() public WebResponse test(@AuthenticationPrincipal UsernamePasswordAuthenticationToken authenticationToken){ // 略 }權限控制
啟用基于方法的權限注解
@SpringBootApplication @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
簡單權限校驗
例如,刪除角色的接口,僅允許擁有 ROLE_ADMIN_USER 權限的用戶訪問。
/** * 刪除角色 */ @PostMapping("/delete") @PreAuthorize("hasRole("ADMIN_USER")") public WebResponse deleteRole(@RequestBody RoleBean roleBean){ // 略 }
@PreAuthorize("hasRole("")") 可作用于微服務中的各個模塊
自定義權限校驗
如上所示,hasRole() 方法是 Spring Security 內嵌的,如需自定義,可以使用 Expression-Based Access Control,示例:
/** * 自定義校驗服務 */ @Service public class CustomService{ public boolean check(UsernamePasswordAuthenticationToken authenticationToken, String extraParam){ // 略 } }
/** * 刪除角色 */ @PostMapping() @PreAuthorize("@customService.check(authentication, #userBean.username)") public WebResponse custom(@RequestBody UserBean userBean){ // 略 }
authentication 屬于內置對象, # 獲取入參的值
任意用戶權限動態修改
原理上,用戶的權限信息保存在 Redis 中,修改用戶權限就需要操作 Redis,示例:
@Service @AllArgsConstructor public class HttpSessionService{ private final FindByIndexNameSessionRepositorysessionRepository; /** * 重置用戶權限 */ public void resetAuthorities(Long userId, Listauthorities){ UsernamePasswordAuthenticationToken newToken = new UsernamePasswordAuthenticationToken(userId, null, authorities); Map redisSessionMap = sessionRepository.findByPrincipalName(String.valueOf(userId)); redisSessionMap.values().forEach(session -> { SecurityContextImpl securityContext = session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); securityContext.setAuthentication(newToken); session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext); sessionRepository.save(session); }); } }
修改用戶權限,僅需調用 httpSessionService.resetAuthorities() 方法即可,實時生效。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/75029.html
摘要:框架具有輕便,開源的優點,所以本譯見構建用戶管理微服務五使用令牌和來實現身份驗證往期譯見系列文章在賬號分享中持續連載,敬請查看在往期譯見系列的文章中,我們已經建立了業務邏輯數據訪問層和前端控制器但是忽略了對身份進行驗證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護REST API 重拾后端之Spring Boot(一):REST API的搭建...
摘要:寫在前面在一款應用的整個生命周期,我們都會談及該應用的數據安全問題。用戶的合法性與數據的可見性是數據安全中非常重要的一部分。 寫在前面 在一款應用的整個生命周期,我們都會談及該應用的數據安全問題。用戶的合法性與數據的可見性是數據安全中非常重要的一部分。但是,一方面,不同的應用對于數據的合法性和可見性要求的維度與粒度都有所區別;另一方面,以當前微服務、多服務的架構方式,如何共享Sessi...
摘要:負載均衡組件是一個負載均衡組件,它通常和配合使用。和配合,很容易做到負載均衡,將請求根據負載均衡策略分配到不同的服務實例中。和配合,在消費服務時能夠做到負載均衡。在默認的情況下,和相結合,能夠做到負載均衡智能路由。 2.2.1 簡介 Spring Cloud 是基于 Spring Boot 的。 Spring Boot 是由 Pivotal 團隊提供的全新 Web 框架, 它主要的特點...
摘要:每個服務運行在其獨立的進程中,服務與服務間采用輕量級的通信機制互相溝通通常是基于的。在微服務架構下,故障會被隔離在單個服務中。 1. 源碼下載地址 源碼鏈接: https://github.com/samt007/xy... 這是用Spring Cloud微服務架構搭建的一套基于EBS的API服務系統如對本文有任何的疑問,請聯系我:samt007@qq.com 2. Introduc...
閱讀 4015·2023-04-26 02:13
閱讀 2260·2021-11-08 13:13
閱讀 2748·2021-10-11 10:59
閱讀 1745·2021-09-03 00:23
閱讀 1314·2019-08-30 15:53
閱讀 2293·2019-08-28 18:22
閱讀 3061·2019-08-26 10:45
閱讀 744·2019-08-23 17:58