我可能比我应该更努力地让它发挥作用,但现在我放弃了。我需要在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) ;
}
然而,在下次请求时没有自动验证,所以我想知道这里有什么问题。
答案 0 :(得分:1)
问题出在autologin
方法中。
请勿直接使用userDetailsService
。改为注入AuthenticationManager
并使用原始authenticate
和UsernamePasswordAuthenticationToken
传递给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%