且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何使用 Spring Boot 和 Spring Security 保护 REST API?

更新时间:2022-10-14 17:53:01

基于令牌的身份验证 - 用户将提供其凭据并获得唯一且有时间限制的访问令牌.我想管理令牌在我自己的实现中创建、检查有效性、到期.

实际上,对令牌身份验证使用过滤器 - 在这种情况下***的方法

最终,您可以通过 Spring Data 创建 CRUD 来管理 Token 的属性,例如过期等.

这是我的令牌过滤器:http://pastebin.com/13WWpLq2

和令牌服务实现

http://pastebin.com/dUYM555E

某些 REST 资源将是公开的 - 根本不需要进行身份验证

这不是问题,您可以像这样通过 Spring 安全配置管理您的资源:.antMatchers("/rest/blabla/**").permitAll()

某些资源只有拥有管理员权限的用户才能访问,

看看 @Secured 对类的注解.示例:

@Controller@RequestMapping(value = "/adminservice")@Secured("ROLE_ADMIN")公共类 AdminServiceController {

其他资源在所有用户授权后即可访问.

回到Spring Security配置,你可以像这样配置你的url:

 http.authorizeRequests().antMatchers("/openforall/**").permitAll().antMatchers("/alsoopen/**").permitAll().anyRequest().authenticated()

我不想使用基本身份验证

是的,通过令牌过滤器,您的用户将通过身份验证.

Java 代码配置(非 XML)

回到上面的话,看@EnableWebSecurity.您的课程将是:

@Configuration@启用网络安全公共类 SecurityConfig 扩展了 WebSecurityConfigurerAdapter {}

您必须覆盖 configure 方法.下面的代码,只是举例,如何配置匹配器.它来自另一个项目.

 @Overrideprotected void configure(HttpSecurity http) 抛出异常 {http.authorizeRequests().antMatchers("/assets/**").permitAll().anyRequest().authenticated().和().formLogin().usernameParameter("j_username").passwordParameter("j_password").loginPage("/登录").defaultSuccessUrl("/", true).successHandler(customAuthenticationSuccessHandler).permitAll().和().登出().logoutUrl("/注销").invalidateHttpSession(true).logoutSuccessUrl("/").deleteCookies("JSESSIONID").logoutRequestMatcher(new AntPathRequestMatcher("/logout")).和().csrf();}

I know that securing REST API is widely commented topic but I'm not able to create a small prototype that meets my criteria (and I need to confirm that these criteria are realistic). There are so many options how to secure resources and how work with Spring security, I need to clarify if my needs are realistic.

My requirements

  • Token based authenticator - users will provide its credentials and get unique and time limited access token. I would like to manage token creation, checking validity, expiration in my own implementation.
  • Some REST resources will be public - no need to authenticate at all,
  • Some resources will be accessible only for users with administrator rights,
  • Other resource will be accessible after authorization for all users.
  • I don't want to use Basic authentication
  • Java code configuration (not XML)

Current status

My REST API works very well, but now I need to secure it. When I was looking for a solution I created a javax.servlet.Filter filter:

  @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        String accessToken = request.getHeader(AUTHORIZATION_TOKEN);
        Account account = accountDao.find(accessToken);

        if (account == null) {    
            throw new UnauthorizedException();    
        }

        chain.doFilter(req, res);

    }

But this solution with javax.servlet.filters doesn't work as I need because there is an issue with exception handling via @ControllerAdvice with Spring servlet dispatcher.

What I need

I would like to know if these criteria are realistic and get any help, how to start securing REST API with Spring Security. I read many tutorials (e.g. Spring Data REST + Spring Security) but all work in very basic configuration - users with their credentials are stored in memory in configuration and I need to work with DBMS and create own authenticator.

Please give me some ideas how to start.

Token based authentication - users will provide its credentials and get unique and time limited access token. I would like to manage token creation, checking validity, expiration in my own implementation.

Actually, use Filter for token Auth - best way in this case

Eventually, you can create CRUD via Spring Data for managing Token's properties like to expire, etc.

Here is my token filter: http://pastebin.com/13WWpLq2

And Token Service Implementation

http://pastebin.com/dUYM555E

Some REST resources will be public - no need to authenticate at all

It's not a problem, you can manage your resources via Spring security config like this: .antMatchers("/rest/blabla/**").permitAll()

Some resources will be accessible only for users with administrator rights,

Take a look at @Secured annotation to class. Example:

@Controller
@RequestMapping(value = "/adminservice")
@Secured("ROLE_ADMIN")
public class AdminServiceController {

The other resource will be accessible after authorization for all users.

Back to Spring Security configure, you can configure your url like this:

    http
            .authorizeRequests()
            .antMatchers("/openforall/**").permitAll()
            .antMatchers("/alsoopen/**").permitAll()
            .anyRequest().authenticated()

I don't want to use Basic authentication

Yep, via token filter, your users will be authenticated.

Java code configuration (not XML)

Back to the words above, look at @EnableWebSecurity. Your class will be:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {}

You have to override the configure method. Code below, just for example, how to configure matchers. It's from another project.

    @Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/assets/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
                .usernameParameter("j_username")
                .passwordParameter("j_password")
                .loginPage("/login")
                .defaultSuccessUrl("/", true)
                .successHandler(customAuthenticationSuccessHandler)
                .permitAll()
            .and()
                .logout()
                .logoutUrl("/logout")
                .invalidateHttpSession(true)
                .logoutSuccessUrl("/")
                .deleteCookies("JSESSIONID")
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .and()
                .csrf();
}