当前位置: 首页 - 行业资讯 - 基于 Spring Session Spring Security 微服务许可权控制

基于 Spring Session Spring Security 微服务许可权控制

2025-01-03 行业资讯 0

基于 Spring Session & Spring Security 微服务许可权控制

微服务架构

闸道器:路由使用者请求到指定服务,转发前端 Cookie 中包含的 Session 资讯;使用者服务:使用者登入认证(Authentication),使用者授权(Authority),使用者管理(Redis Session Management)其他服务:依赖 Redis 中使用者资讯进行界面请求验证使用者 - 角色 - 许可权表结构设计

许可权表许可权表最小粒度的控制单个功能,例如使用者管理、资源管理,表结构示例:

角色 - 许可权表自定义角色,组合各种许可权,例如超级管理员拥有所有许可权,表结构示例:

使用者 - 角色表使用者系结一个或多个角色,即分配各种许可权,示例表结构:

使用者服务设计

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

List userAuthorities = 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 FindByIndexNameSessionRepository sessionRepository;

/**

* 重置使用者许可权

*/

public void resetAuthorities(Long userId, List authorities){

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() 方法即可,实时生效。

标签: 科技行业资讯

上一篇:仪器仪表的重要性精确度安全性与高效生产的关键

下一篇:情人节的甜蜜誓言玫瑰巧克力与永恒爱

相关推荐
推荐资讯
热门文章