我使用OAuth2实现了Spring Boot Web应用程序的登录,并且一切正常。 唯一的问题是登录的用户没有保存在会话内的权限信息,因此每次我请求url时,控制器都将其注释@PreAuthorize(“ hasRole('USER')”)拒绝。>
SecurityConfiguration类:
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@EnableJpaRepositories(basePackageClasses = UserRepository.class)
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private CustomOAuth2UserService customOAuth2UserService;
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true")
.and()
.logout()
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.and()
.oauth2Login()
.loginPage("/login")
.failureUrl("/login?error=true")
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.failureHandler(oAuth2AuthenticationFailureHandler);
}
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
这是CustomOAuth2UserService类:
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
@Autowired
private UserService userService;
@Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(oAuth2UserRequest);
try {
return processOAuth2User(oAuth2UserRequest, oAuth2User);
}catch (Exception ex) {
// Throwing an instance of AuthenticationException will trigger the OAuth2AuthenticationFailureHandler
throw new InternalAuthenticationServiceException(ex.getMessage(), ex.getCause());
}
}
private OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) {
OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(oAuth2UserRequest.getClientRegistration().getRegistrationId(), oAuth2User.getAttributes());
if(StringUtils.isEmpty(oAuth2UserInfo.getEmail())) {
throw new RuntimeException("Id not found from OAuth2 provider");
}
User user;
try {
user = userService.getByEmail(oAuth2UserInfo.getEmail());
if(!user.getProvider().toString().equalsIgnoreCase(oAuth2UserRequest.getClientRegistration().getRegistrationId())) throw new EmailAlreadyTakenException("email-already-taken");
} catch (UserNotFoundException e) {
user = registerNewUser(oAuth2UserRequest, oAuth2UserInfo);
}
return new CustomUserDetails(user);
}
private User registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo oAuth2UserInfo) {
User user = new User();
user.setProvider(AuthProvider.valueOf(oAuth2UserRequest.getClientRegistration().getRegistrationId()));
Identity identity = new Identity(user);
if(oAuth2UserInfo.getFirstName() != null && !oAuth2UserInfo.getFirstName().equalsIgnoreCase(""))
identity.setFirstName(oAuth2UserInfo.getFirstName());
if(oAuth2UserInfo.getLastName() != null && !oAuth2UserInfo.getLastName().equalsIgnoreCase(""))
identity.setSecondName(oAuth2UserInfo.getLastName());
user.setIdentity(identity);
user.setEmail(oAuth2UserInfo.getEmail());
user.setConfirmedRegistration(true);
boolean flag = false;
String username = oAuth2UserInfo.getName().toLowerCase().replaceAll("\\s+", "");
user.setUsername(username);
return userService.addFacebookUser(user);
}
}
这是application.properties文件的一部分:
spring.security.oauth2.client.registration.facebook.client-id=***
spring.security.oauth2.client.registration.facebook.client-secret=***
spring.security.oauth2.client.registration.facebook.scope=email,public_profile
spring.security.oauth2.client.registration.google.client-id=***
spring.security.oauth2.client.registration.google.client-secret=***
spring.security.oauth2.client.registration.google.scope=email,profile
spring.security.oauth2.client.provider.facebook.authorizationUri = https://www.facebook.com/v3.0/dialog/oauth
spring.security.oauth2.client.provider.facebook.tokenUri = https://graph.facebook.com/v3.0/oauth/access_token
spring.security.oauth2.client.provider.facebook.userInfoUri = https://graph.facebook.com/v3.0/me?fields=id,first_name,middle_name,last_name,name,email,verified,is_verified,picture
登录后,用户可以调用该网址/ users / {username},但是当他通过OAuth2用facebook或google登录时,他被拒绝了,因为权限列表为空。当他使用自己的webapp凭据登录时,权限列表中包含USER_ROLE,他可以继续进行操作。
@PreAuthorize("hasRole('USER')")
@GetRequest("users/{username}")
public String getUser(@PathVariable String username, @PathVariable String subsection, Model model, Principal principal) throws IllegalAccessException, UserNotFoundException {
User user = userService.getByUsername(principal.getName());
model.addAttribute("user", user);
return "user";
}
内部主要对象有:
使用OAuth2登录时:
使用本地凭据登录时:
答案 0 :(得分:0)
经过一段时间的调试,我找到了解决方案!我没有正确配置用户角色。 在我的自定义OAuth2UserService的registerNewUser方法内部,我没有设置用户角色。我刚刚添加了这一行:
user.setRoles(new HashSet<>(Collections.singletonList(new Role("ROLE_USER"))));
,一切开始起作用!因此,现在当询问OAuth2User的权限时,它仅调用CustomUserDetails的getAuthorities(我的OAuth2User的实现),并调用User的getRoles方法。
CustomUserDetails类:
public class CustomUserDetails extends User implements UserDetails, OAuth2User {
public CustomUserDetails() {
}
public CustomUserDetails(String username, String email, String password, Set<Role> roles) {
super(username, email, password, roles);
}
public CustomUserDetails(User user) {
super(user.getUsername(), user.getEmail(), user.getPassword(), user.getRoles());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles()
.stream()
.map(role -> new SimpleGrantedAuthority(role.getRole()))
.collect(Collectors.toList());
}
@Override
public Map<String, Object> getAttributes() {
return null;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String getName() {
return null;
}
}