启用globalmethodsecurity+restapi+header auth token+redis store的Spring Security

7xzttuei  于 2021-06-08  发布在  Redis
关注(0)|答案(0)|浏览(202)

我想用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);
    }
}

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题