我想用globalmethodsecurity将下面的方面解决方案实现到Spring Security 中。在数据库和redis之间手动执行身份验证。基本上,我创建一个令牌、过期和角色集。
每个用户都有(或没有)“x-auth-token”头。如果它在使用redis存储的数据时存在。如果这个redis记录不存在,我们就说“notauthenteduser.”ofc,如果这个redis记录被删除就相当于注销。所以 Spring 安全认证不是永久性的。每个请求都要生成一个身份验证,即使是对同一个用户。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Authorized {
String[] rules() default {};
}
@RestController
@RequestMapping("management/user")
public class UserRestController {
@Authorized("ANY_ROLE") // plan --> @PreAuthorize("hasRole('ROLE_ANY_ROLE')")
@GetMapping("test")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void test() {
}
}
@Data
@RedisHash("ManagedAuthSession")
public class ManagedAuthSession {
@Id
private String email;
@Indexed
private String token; // findByToken(token) called by getSessionByToken(token)
private Instant exp;
private Set<String> rules;
public final Set<SimpleGrantedAuthority> getAuthorities() {
return rules.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet());
}
}
@Service
public class ManagementUserService {
// ...
public Optional<ManagedAuthSession> getSessionByToken(@NonNull String token) {
return this.managedAuthSessionRepository.findByToken(token);
}
// ...
}
这个“authorizedimpl”我想进入spring安全解决方案。
@Aspect
@Component
public class AuthorizedImpl {
private final ManagementUserService managementUserService;
AuthorizedImpl(
ManagementUserService managementUserService
) {
this.managementUserService = managementUserService;
}
@Before("@annotation(com.xxx.yyy.Authorized)")
public void before(JoinPoint jp) throws InvalidTokenException, PermissionDeniedException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String token = request.getHeader("x-auth-token");
if (token == null) {
throw new InvalidTokenException();
}
// throw PermissionDeniedException if not valid
managementUserService.isTokenValid(token);
Authorized authorized = ((MethodSignature) jp.getSignature()).getMethod().getAnnotation(Authorized.class);
Set<String> rules = new HashSet<>(Arrays.asList(authorized.rules()));
ManagedAuthSession session = managementUserService.getSessionByToken(token).orElseThrow(PermissionDeniedException::new);
rules.removeAll(Optional.ofNullable(session.getRules()).orElse(new HashSet<>()));
if (0 < rules.size()) {
throw new PermissionDeniedException();
}
}
}
@RestControllerAdvice
public class ErrorRestControllerAdvice {
// ...
@ExceptionHandler({
PermissionDeniedException.class, // extends ForbiddenException
InvalidTokenException.class // extends ForbiddenException
})
@ResponseStatus(HttpStatus.FORBIDDEN)
public ErrorRestResponse forbidden(ForbiddenException exception) {
return new ErrorRestResponse(
exception,
applicationProperties.getError().isIncludeStacktrace()
);
}
}
我读了很多博客、描述和stackoverflow,但每个人都不同意我的想法。它不起作用,我也不知道什么是最佳解决方案。
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final ManagementUserService managementUserService;
@Value("#{'${application.security.hostname-allowed}'.split(',')}")
private List<String> allowedHostnames;
public SecurityConfigDev(
ManagementUserService managementUserService
) {
super();
this.managementUserService = managementUserService;
}
@Override
protected void configure(@NonNull HttpSecurity http)
throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling().and()
.authorizeRequests().anyRequest().authenticated().and()
.cors().and()
.anonymous().disable()
.rememberMe().disable()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
http.addFilterBefore(filter(), AnonymousAuthenticationFilter.class);
}
@Override
public void configure(@NonNull WebSecurity web) {
web.httpFirewall(getFirewall(allowedHostnames));
}
private TokenAuthenticationFilter filter() {
return new TokenAuthenticationFilter(managementUserService);
}
}
public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private final ManagementUserService managementUserService;
public TokenAuthenticationFilter(
ManagementUserService managementUserService
) {
super(new OrRequestMatcher(new AntPathRequestMatcher("/**"))); // ?
this.managementUserService = managementUserService;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws AuthenticationException {
System.out.println(0);
return Optional.ofNullable(httpServletRequest.getHeader("x-auth-token"))
.flatMap(managementUserService::getSessionByToken)
.map((var session) -> new AnonymousAuthenticationToken(session.getToken(), session.getEmail(), session.getAuthorities()))
.map((var user) -> getAuthenticationManager().authenticate(user))
.orElse(null);
}
@Override
protected void unsuccessfulAuthentication(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException failed
) throws IOException {
ErrorJsonResponse.create(response, HttpStatus.FORBIDDEN);
}
@Override
protected void successfulAuthentication(
final HttpServletRequest request,
final HttpServletResponse response,
final FilterChain chain,
final Authentication authResult
) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
暂无答案!
目前还没有任何答案,快来回答吧!