Spring Безопасность @AuthenticationPrincipal
Я пытаюсь заставить @AuthenticationPrincipal работать с пользовательским классом User. К сожалению, пользователь всегда имеет значение null. Здесь код:
контроллер
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(@AuthenticationPrincipal User user) {
ModelAndView mav= new ModelAndView("/web/index");
mav.addObject("user", user);
return mav;
}
Конфигурация безопасности
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
CustomUserDetailsService customUserDetailsService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
}
}
CustomUserDetailsService
@Component
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// Spring Data findByXY function
return userRepository.findByUsername(username);
}
Пользовательский
public class User implements UserDetails{
private String username;
private String password;
private Collection<Authority> authorities;
// Getters and Setters
}
Орган управления
public class Authority implements GrantedAuthority{
private User user;
private String role;
// Getters and Setters
@Override
public String getAuthority() {
return this.getRole();
}
}
Я пробовал различные решения, которые я нашел в Интернете, например. преобразование моего пользовательского объекта пользователя следующим образом:
return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), true, true, true, true, authorities);
Другие способы получения активных пользователей работают без проблем, но я считаю, что @AuthenticationProvider CustomUserObject является самым чистым способом, поэтому я хотел бы заставить это работать. Любая помощь приветствуется.
Ответы
Ответ 1
Вместо использования @AuthenticationPrincipal вы можете напрямую указать свою зависимость для аутентифицированного пользователя в аргументе метода. что-то ниже
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(Principal user) {
ModelAndView mav= new ModelAndView("/web/index");
mav.addObject("user", user);
return mav;
}
Этот Основной объект будет фактическим объектом, который получил аутентификацию через spring безопасность. spring будет вводить это для вас, когда метод будет вызван.
Ответ 2
В моем случае я получаю String
назад (имя пользователя), а не объект UserDetails
, т.е. вы должны определить подпись метода как
public ModelAndView index(@AuthenticationPrincipal String username)
Это не странно, так как @AuthenticationPrincipal
в действительности возвращает Authentication.getPrincipal()
и в соответствии с документацией:
В случае запроса аутентификации с именем пользователя и паролем, это будет имя пользователя. Ожидается, что абоненты принципала для запроса аутентификации.
Реализация AuthenticationManager часто возвращает Аутентификация, содержащая более богатую информацию в качестве принципала для использования по заявке. Многие поставщики аутентификации UserDetails - объект в качестве принципала. См.: https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/api/
Итак, я предполагаю, что ваша реализация AuthenticationManager
возвращает только имя пользователя
Ответ 3
@RequestMapping(method= RequestMethod.GET,value="/authenticate", produces = MediaType.APPLICATION_JSON_VALUE)
public Object authenticate(@AuthenticationPrincipal Object obj) {
return obj;
}
У меня та же проблема, я смог выполнить эту работу.
Вы можете использовать его с mapper
@RequestMapping(method = RequestMethod.GET, value = "/authenticate2", produces = MediaType.APPLICATION_JSON_VALUE)
public User authenticate2(@AuthenticationPrincipal Object obj) throws IOException {
return mapper.readValue(mapper.writeValueAsString(obj), User.class);
}
Они работали для меня, я надеюсь, что это сработает для любого в будущем
Ответ 4
Вам нужно проверить, используете ли вы правильную версию аннотации @AuthenticationPrincipal
и правильную версию класса AuthenticationPrincipalArgumentResolver
.
До версии 4.0 Spring Безопасность вам пришлось использовать классы:
Начиная с версии 4.0, вы должны использовать:
Посмотрите @AuthenticationPrincipal официальные документы для примеров конфигурации.
Ответ 5
Я нашел другое решение, даже это не канонический.
В вашем контроллере
@RequestMapping(value = "/login", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<UserDTO> login(@RequestBody UserDTO user){
try {
usersService.authenticate(user.getUsername(), user.getPassword());
return new ResponseEntity<UserDTO>(user, HttpStatus.OK);
}
catch (BadCredentialsException e){
return new ResponseEntity<UserDTO>(HttpStatus.UNAUTHORIZED);
}
}
Если UserDTO имеет только форму, содержащую имя пользователя и пароль
В вашем CustomUserDetailsService
public void authenticate(String username, String password){
try{
User user = new User(username, password);
Authentication request = new UsernamePasswordAuthenticationToken(user, password, Arrays.asList(WebSecurityConfiguration.USER));
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
} catch (InternalAuthenticationServiceException e){
// treat as a bad credential
}
}
Внедрить собственный AuthenticationManager
@Component
class DefaultAuthenticationManager implements AuthenticationManager {
@Autowired
private CustomUserDetailsService usersService;
public Authentication authenticate(Authentication auth) throws AuthenticationException {
UserDetails user = usersService.loadUserByUsername(((User)auth.getPrincipal()).getUsername());
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
}
// Handle bad credentials here
}
}
Принципиальным является то, что Principal
в CustomUserDetailsService#authenticate
- это объект, а не имя вашего аутентифицированного пользователя, так что структура может обрабатывать его, а затем вводить через механизм @AuthenticationPrincipal
. Это сработало для меня.
Ответ 6
Я столкнулся с той же проблемой, что и вы.
по какой-то причине spring security @EnableWebSecurity не добавлять аргументResolver автоматически, вам нужно добавить его вручную:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
Ответ 7
Я достигаю этого с помощью поставщика аутентификации, который расширяет AbstractUserDetailsAuthenticationProvider.
В моем провайдере я переопределяю следующий метод:
Защищенный UserDetails retrieveUser ( Строковое имя пользователя, Имя пользователяPasswordAuthenticationToken аутентификация) создает исключение AuthenticationException {}
и в этом методе я создаю свою реализацию UserDetails и возвращаю ее. Затем я могу получить его в контроллере с аннотацией AuthenticationPrincipal.
Ответ 8
Хотя в документации упоминается о customUser, я не думаю, что это возможно. Используйте @AuthenticationPrincipal
с java.lang.Object
или org.springframework.security.core.userdetails.User
в качестве типа параметра метода контроллера пружины.
Если типы не совпадают, возвращается ноль, если только AuthenticationPrincipal.errorOnInvalidType() имеет значение true, и в этом случае ClassCastException будет брошен. Spring API Документация