如何使用Spring Security实现RESTful登录API?

huangapple go评论75阅读模式
英文:

How to do a RESTful login API using Spring Security?

问题

I just started to work with Spring Security and just implemented HTTP basic authentication that works fine. Now I would like to create a login endpoint and try to get an authenticated user. I am not sure what I have to use for that? I did read online lots of stuff but not sure where to start. Any tip would be very appreciated!

This is the code that used for authentication.

SecurityConfiguration.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .userDetailsService(userDetailsService)
            .passwordEncoder(encodePWD());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http
            .csrf().disable();
        http
            .httpBasic()
                .and()
            .authorizeRequests()
                .antMatchers("/rest/**").permitAll()
                .and()
            .authorizeRequests()
                .antMatchers("/secure/**").hasAnyRole("ADMIN")
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .permitAll();
    }

    @Bean
    public BCryptPasswordEncoder encodePWD() {
        return new BCryptPasswordEncoder();
    }
}

CustomUserDetailsService.java

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        CustomUserDetails userDetails = null;

        if(user != null){
            userDetails = new CustomUserDetails();
            userDetails.setUser(user);
        }else{
            throw new UserNotFoundException("User doesn't exist with this username: " + username);
        }
        return userDetails;
    }
}

CustomUserDetails.java

@Getter
@Setter
public class CustomUserDetails implements UserDetails {

    private User user;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getRoles().stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRole())).collect(Collectors.toSet());

    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

UPDATE

I am sending my data like JSON using Postman

{
    "username": "admin",
    "password": "admin35"      
}
英文:

I just started to work with Spring security and just implemented HTTP basic authentication that works fine.

Now I would like to create login endpoint and try to get authenticated user.

I am not sure what do I have to use for that? I did read online lots of stuff but not sure where to start.

Any tip would be very appreciated!

This is the code that used for authentication.

SecurityConfiguration.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

   @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .userDetailsService(userDetailsService)
            .passwordEncoder(encodePWD());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http
            .csrf().disable();
        http
            .httpBasic()
                .and()
            .authorizeRequests()
                .antMatchers(&quot;/rest/**&quot;).permitAll()
                .and()
            .authorizeRequests()
                .antMatchers(&quot;/secure/**&quot;).hasAnyRole(&quot;ADMIN&quot;)
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .permitAll();
    }

    @Bean
    public BCryptPasswordEncoder encodePWD() {
        return new BCryptPasswordEncoder();
    }
}

CustomUserDetailsService.java

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        CustomUserDetails userDetails = null;

        if(user != null){
            userDetails = new CustomUserDetails();
            userDetails.setUser(user);
        }else{
            throw new UserNotFoundException(&quot;User doesn&#39;t exist with this username: &quot; + username);
        }
        return userDetails;
    }
}

CustomUserDetails.java

@Getter
@Setter
public class CustomUserDetails implements UserDetails {

    private User user;

    @Override
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        return user.getRoles().stream().map(role -&gt; new SimpleGrantedAuthority(&quot;ROLE_&quot; + role.getRole())).collect(Collectors.toSet());

    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

UPDATE

I am sending my data like JSON using Postman

{
    &quot;username&quot;: &quot;admin&quot;,
    &quot;password&quot;: &quot;admin35&quot;      
}

答案1

得分: 1

在传统的MVC Spring Boot应用中,Spring Security会检查SecurityContextHolder以获取认证信息。如果未找到信息,那么会将您重定向到登录页面。

对于REST API端点,您不会从服务器收到典型的登录表单。您会从客户端(如AngularJS、ReactJS、VueJs、iOS应用等)收集凭据,然后将凭据POST到一个端点以检索用户信息。例如,以下代码对用户进行了身份验证,然后将经过身份验证的用户信息返回给客户端。

private static final Logger logger = LoggerFactory.getLogger(UserController.class);

@GetMapping("/user")
public ResponseEntity<UserResponse> getUser(@AuthenticationPrincipal DmsUserDetails dmsUser) {

    logger.info("User " + dmsUser.getFirstName() + " " + dmsUser.getLastName() + " logged in.");

    UserResponse user = new UserResponse();
    user.setUserId(dmsUser.getUserId());
    user.setFirstName(dmsUser.getFirstName());
    user.setLastName(dmsUser.getLastName());
    user.setEmail(dmsUser.getUsername());

    return ResponseEntity.ok(user);
}

客户端会跟踪用户是否已登录,并在每次后续请求中传递cookie或令牌。

在这种情况下,我将端点命名为/user,但如果您愿意,也可以将其命名为/login

英文:

In a traditional MVC Spring Boot application, Spring Security would check the SecurityContextHolder for the authentication information. If not found, then you would be redirected to a login page.

For a REST API endpoint, you would not have a typical login form sent from the server. You would collect the credentials from a form on the client (AngularJS, ReactJS, VueJs, iOS app, etc) and POST the credentials to an endpoint to retrieve user information. For example, the following authenticates the user, then returns the authenticated user information to the client.

private static final Logger logger = LoggerFactory.getLogger(UserController.class);
	
	@GetMapping(&quot;/user&quot;)
	public ResponseEntity&lt;UserResponse&gt; getUser(@AuthenticationPrincipal DmsUserDetails dmsUser) {
		
		logger.info(&quot;User &quot; + dmsUser.getFirstName() + &quot; &quot; + dmsUser.getLastName() + &quot; logged in.&quot;);
		
		UserResponse user = new UserResponse();
		user.setUserId(dmsUser.getUserId());
		user.setFirstName(dmsUser.getFirstName());
		user.setLastName(dmsUser.getLastName());
		user.setEmail(dmsUser.getUsername());
		
		return ResponseEntity.ok(user);
	}

The client would keep track of whether the user is logged in, and pass either a cookie or a token on each subsequent request.

In this case, I've named the endpoint /user, but you can call it /login if you prefer.

huangapple
  • 本文由 发表于 2020年9月2日 04:28:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/63695023.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定