摘要:構(gòu)造函數(shù)的第一個(gè)參數(shù)是對(duì)象,所以可以自定義緩存對(duì)象。在微服務(wù)各個(gè)模塊獲取用戶的這些信息的方法如下略權(quán)限控制啟用基于方法的權(quán)限注解簡(jiǎn)單權(quán)限校驗(yàn)例如,刪除角色的接口,僅允許擁有權(quán)限的用戶訪問(wèn)。
微服務(wù)架構(gòu)
網(wǎng)關(guān):路由用戶請(qǐng)求到指定服務(wù),轉(zhuǎn)發(fā)前端 Cookie 中包含的 Session 信息;
用戶服務(wù):用戶登錄認(rèn)證(Authentication),用戶授權(quán)(Authority),用戶管理(Redis Session Management)
其他服務(wù):依賴 Redis 中用戶信息進(jìn)行接口請(qǐng)求驗(yàn)證
用戶 - 角色 - 權(quán)限表結(jié)構(gòu)設(shè)計(jì)權(quán)限表
權(quán)限表最小粒度的控制單個(gè)功能,例如用戶管理、資源管理,表結(jié)構(gòu)示例:
id | authority | description |
---|---|---|
1 | ROLE_ADMIN_USER | 管理所有用戶 |
2 | ROLE_ADMIN_RESOURCE | 管理所有資源 |
3 | ROLE_A_1 | 訪問(wèn) ServiceA 的某接口的權(quán)限 |
4 | ROLE_A_2 | 訪問(wèn) ServiceA 的另一個(gè)接口的權(quán)限 |
5 | ROLE_B_1 | 訪問(wèn) ServiceB 的某接口的權(quán)限 |
6 | ROLE_B_2 | 訪問(wèn) ServiceB 的另一個(gè)接口的權(quán)限 |
角色 - 權(quán)限表
自定義角色,組合各種權(quán)限,例如超級(jí)管理員擁有所有權(quán)限,表結(jié)構(gòu)示例:
id | name | authority_ids |
---|---|---|
1 | 超級(jí)管理員 | 1,2,3,4,5,6 |
2 | 管理員A | 3,4 |
3 | 管理員B | 5,6 |
4 | 普通用戶 | NULL |
用戶 - 角色表
用戶綁定一個(gè)或多個(gè)角色,即分配各種權(quán)限,示例表結(jié)構(gòu):
user_id | role_id |
---|---|
1 | 1 |
1 | 4 |
2 | 2 |
Maven 依賴(所有服務(wù))
org.springframework.boot spring-boot-starter-security org.springframework.session spring-session-data-redis
應(yīng)用配置 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=
用戶登錄認(rèn)證(authentication)與授權(quán)(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("登錄失敗"); } /** * 獲取請(qǐng)求體 */ 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()); } /** * 校驗(yàn)用戶名和密碼 */ 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); } /** * 自定義認(rèn)證過(guò)濾器 */ private CustomAuthenticationFilter customAuthenticationFilter() { CustomAuthenticationFilter authenticationFilter = new CustomAuthenticationFilter(LOGIN_URL, userService); return authenticationFilter; } }其他服務(wù)設(shè)計(jì)
應(yīng)用配置 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(); } }用戶認(rèn)證信息獲取
用戶通過(guò)用戶服務(wù)登錄成功后,用戶信息會(huì)被緩存到 Redis,緩存的信息與 CustomAuthenticationFilter 中 attemptAuthentication() 方法返回的對(duì)象有關(guān),如上所以,返回的對(duì)象是 new UsernamePasswordAuthenticationToken(user.getId(), null, userAuthorities),即 Redis 緩存了用戶的 ID 和用戶的權(quán)力(authorities)。
UsernamePasswordAuthenticationToken 構(gòu)造函數(shù)的第一個(gè)參數(shù)是 Object 對(duì)象,所以可以自定義緩存對(duì)象。
在微服務(wù)各個(gè)模塊獲取用戶的這些信息的方法如下:
@GetMapping() public WebResponse test(@AuthenticationPrincipal UsernamePasswordAuthenticationToken authenticationToken){ // 略 }權(quán)限控制
啟用基于方法的權(quán)限注解
@SpringBootApplication @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
簡(jiǎn)單權(quán)限校驗(yàn)
例如,刪除角色的接口,僅允許擁有 ROLE_ADMIN_USER 權(quán)限的用戶訪問(wèn)。
/** * 刪除角色 */ @PostMapping("/delete") @PreAuthorize("hasRole("ADMIN_USER")") public WebResponse deleteRole(@RequestBody RoleBean roleBean){ // 略 }
@PreAuthorize("hasRole("")") 可作用于微服務(wù)中的各個(gè)模塊
自定義權(quán)限校驗(yàn)
如上所示,hasRole() 方法是 Spring Security 內(nèi)嵌的,如需自定義,可以使用 Expression-Based Access Control,示例:
/** * 自定義校驗(yàn)服務(wù) */ @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 屬于內(nèi)置對(duì)象, # 獲取入?yún)⒌闹?/pre>任意用戶權(quán)限動(dòng)態(tài)修改
原理上,用戶的權(quán)限信息保存在 Redis 中,修改用戶權(quán)限就需要操作 Redis,示例:
@Service @AllArgsConstructor public class HttpSessionService{ private final FindByIndexNameSessionRepositorysessionRepository; /** * 重置用戶權(quán)限 */ 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); }); } } 修改用戶權(quán)限,僅需調(diào)用 httpSessionService.resetAuthorities() 方法即可,實(shí)時(shí)生效。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/75029.html
摘要:框架具有輕便,開(kāi)源的優(yōu)點(diǎn),所以本譯見(jiàn)構(gòu)建用戶管理微服務(wù)五使用令牌和來(lái)實(shí)現(xiàn)身份驗(yàn)證往期譯見(jiàn)系列文章在賬號(hào)分享中持續(xù)連載,敬請(qǐng)查看在往期譯見(jiàn)系列的文章中,我們已經(jīng)建立了業(yè)務(wù)邏輯數(shù)據(jù)訪問(wèn)層和前端控制器但是忽略了對(duì)身份進(jìn)行驗(yàn)證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護(hù)REST API 重拾后端之Spring Boot(一):REST API的搭建...
摘要:寫(xiě)在前面在一款應(yīng)用的整個(gè)生命周期,我們都會(huì)談及該應(yīng)用的數(shù)據(jù)安全問(wèn)題。用戶的合法性與數(shù)據(jù)的可見(jiàn)性是數(shù)據(jù)安全中非常重要的一部分。 寫(xiě)在前面 在一款應(yīng)用的整個(gè)生命周期,我們都會(huì)談及該應(yīng)用的數(shù)據(jù)安全問(wèn)題。用戶的合法性與數(shù)據(jù)的可見(jiàn)性是數(shù)據(jù)安全中非常重要的一部分。但是,一方面,不同的應(yīng)用對(duì)于數(shù)據(jù)的合法性和可見(jiàn)性要求的維度與粒度都有所區(qū)別;另一方面,以當(dāng)前微服務(wù)、多服務(wù)的架構(gòu)方式,如何共享Sessi...
摘要:負(fù)載均衡組件是一個(gè)負(fù)載均衡組件,它通常和配合使用。和配合,很容易做到負(fù)載均衡,將請(qǐng)求根據(jù)負(fù)載均衡策略分配到不同的服務(wù)實(shí)例中。和配合,在消費(fèi)服務(wù)時(shí)能夠做到負(fù)載均衡。在默認(rèn)的情況下,和相結(jié)合,能夠做到負(fù)載均衡智能路由。 2.2.1 簡(jiǎn)介 Spring Cloud 是基于 Spring Boot 的。 Spring Boot 是由 Pivotal 團(tuán)隊(duì)提供的全新 Web 框架, 它主要的特點(diǎn)...
摘要:每個(gè)服務(wù)運(yùn)行在其獨(dú)立的進(jìn)程中,服務(wù)與服務(wù)間采用輕量級(jí)的通信機(jī)制互相溝通通常是基于的。在微服務(wù)架構(gòu)下,故障會(huì)被隔離在單個(gè)服務(wù)中。 1. 源碼下載地址 源碼鏈接: https://github.com/samt007/xy... 這是用Spring Cloud微服務(wù)架構(gòu)搭建的一套基于EBS的API服務(wù)系統(tǒng)如對(duì)本文有任何的疑問(wèn),請(qǐng)聯(lián)系我:samt007@qq.com 2. Introduc...
閱讀 4001·2023-04-26 02:13
閱讀 2244·2021-11-08 13:13
閱讀 2729·2021-10-11 10:59
閱讀 1732·2021-09-03 00:23
閱讀 1301·2019-08-30 15:53
閱讀 2275·2019-08-28 18:22
閱讀 3050·2019-08-26 10:45
閱讀 726·2019-08-23 17:58