如何在spring boot中正确配置会话?

时间:2017-06-24 17:34:37

标签: spring spring-mvc spring-boot spring-security

我可能比我应该更努力地让它发挥作用,但现在我放弃了。我需要在Spring Boot项目上进行简单的基于HttpSession的身份验证,这应该基于docs而非常简单。

这是我的'WebSecurityConfigurerAdapter'实现:

package com.collective.foundation ;

import org.springframework.beans.factory.annotation.Autowired ;
import org.springframework.context.annotation.Configuration ;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder ;
import org.springframework.security.config.annotation.web.builders.HttpSecurity ;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity ;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter ;
import org.springframework.security.core.userdetails.UserDetails ;
import org.springframework.security.core.userdetails.UserDetailsService ;
import org.springframework.security.core.userdetails.UsernameNotFoundException ;
import org.springframework.security.config.http.SessionCreationPolicy ;

import com.collective.foundation.usermanagement.UserDetailsServiceImpl ;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService ;

    @Override
    protected UserDetailsService userDetailsService () {
       return this.userDetailsService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
        .antMatchers("/market/**").access("hasRole('CUSTOMER')")
        .antMatchers("/scm/staff/**").access("hasRole('SCM_STAFF')")
        .antMatchers("/scm/manager/**").access("hasRole('SCM_MANAGER')")
        .antMatchers("/warehouse/staff/**").access("hasRole('WAREHOUSE_STAFF')")
        .antMatchers("/warehouse/manager/**").access("hasRole('WAREHOUSE_MANAGER')")
        .antMatchers("/warehouse/supplier/**").access("hasRole('SUPPLIER')")
        .antMatchers("/user/**").permitAll()
        .antMatchers("/**").permitAll()
        // .and()
        //     .formLogin()
        //         .loginPage("/user/login/")
        //         .permitAll()
        .and()
            .logout()
                .permitAll()
        .and()
            .exceptionHandling()
                .accessDeniedPage("/err/403/")
        .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) ;
    }

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

我有一个带有role字段的用户模型来封装主体数据,所以这是我的UserDetailsService实现:

package com.collective.foundation.usermanagement ;

import org.springframework.beans.factory.annotation.Autowired ;
import org.springframework.security.core.GrantedAuthority ;
import org.springframework.security.core.authority.SimpleGrantedAuthority ;
import org.springframework.security.core.userdetails.UserDetails ;
import org.springframework.security.core.userdetails.UserDetailsService ;
import org.springframework.security.core.userdetails.UsernameNotFoundException ;
import org.springframework.stereotype.Service ;
import org.springframework.transaction.annotation.Transactional ;

import java.util.HashSet ;
import java.util.Set ;

import com.collective.foundation.entities.* ;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserRepository userRepository ;

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findOne(username) ;

        Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
        grantedAuthorities.add(new SimpleGrantedAuthority(user.getRole()));

        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities);
    }
}

我依靠自己的控制器来处理登录页面,这是方法:

@PostMapping(value="login/")
public String submitLoginView(@ModelAttribute LoginDto loginDto) {
    //
    System.out.println(Constants.LOG_PREFIX + "submitLoginView") ;
    System.out.println(loginDto.toString()) ;
    //
    User user = userRepository.findOne(loginDto.getUsername()) ;
    if (user != null && user.getPassword().equals(loginDto.getPassword())) {
        System.out.println(Constants.LOG_PREFIX + "successful login") ;
        securityService.autologin(loginDto.getUsername(), loginDto.getPassword()) ;
        return "view_home_page" ;
    } else {
        System.out.println(Constants.LOG_PREFIX + "no matching login found") ;
        return "redirect:/user/login/err/" ;
    }
}

上述控制器中的autoLogin方法只是将从USerDetails获取的userDetailsService实例放入SecurityContext

public void autologin(String username, String password) {
    UserDetails userDetails = userDetailsService.loadUserByUsername(username) ;
    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities()) ;
    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken) ;
}

然而,在下次请求时没有自动验证,所以我想知道这里有什么问题。

1 个答案:

答案 0 :(得分:1)

问题出在autologin方法中。

请勿直接使用userDetailsService。改为注入AuthenticationManager并使用原始authenticateUsernamePasswordAuthenticationToken传递给username方法password。 它将调用userDetailsService,执行所有检查,如果凭据有效,帐户未锁定等等 - 返回Authentication,可以设置为SecurityContext

以下是您的示例的简化版本:

@SpringBootApplication
public class So44739147Application {

    public static void main(String[] args) {
        SpringApplication.run(So44739147Application.class, args);
    }

    @EnableWebSecurity
    static class Security extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable().authorizeRequests()
                    .antMatchers("/login").permitAll()
                    .antMatchers("/protected/*").fullyAuthenticated()
                    .and().logout().permitAll().and().exceptionHandling().accessDeniedPage("/err/403/").and()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        }
    }

    @Service
    public static class SecurityService {
        private final AuthenticationManager authenticationManager;

        @Autowired
        public SecurityService(AuthenticationManager authenticationManager) {
            this.authenticationManager = authenticationManager;
        }

        void autologin(String username, String password) {
            final Authentication usernamePasswordAuthenticationToken = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        }
    }

    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
    public static class CredentialsDTO {
        String username;
        String password;
    }

    @RestController
    @RequestMapping("/login")
    public static class LoginApi {
        private final SecurityService securityService;

        @Autowired
        public LoginApi(SecurityService securityService) {
            this.securityService = securityService;
        }

        @PostMapping
        public String login(@RequestBody CredentialsDTO credentials) {
            securityService.autologin(credentials.username, credentials.password);
            return "ok";
        }
    }

    @RestController
    @RequestMapping("/protected/me")
    public static class MeApi {
        @GetMapping
        public String me(Principal principal) {
            return principal.getName();
        }
    }
}

=&GT;

$ curl -i -XPOST 'localhost:8080/login' -H'Content-Type: application/json' -d'{"username":"user","password":"123"}'
HTTP/1.1 200 
...
Set-Cookie: JSESSIONID=1FECEF3B13065A8938A4B8DA951ED96F; Path=/; HttpOnly
...

ok%
$ curl -XGET 'localhost:8080/protected/me' -H'Cookie: JSESSIONID=1FECEF3B13065A8938A4B8DA951ED96F'                 
user%