[英]Spring Security Custom Login And Custom Authentication Provider
[英]How to fetch custom user entity for custom authentication in login on spring security?
我正在嘗試使用Spring安全性設置一個自定義身份驗證和自定義用戶實體。 如何設置配置權限,以便UserRepository能夠從數據庫中提取用戶信息?
我看過一些關於如何使自定義用戶實體進行身份驗證和登錄的春季教程,而不是使用spring安全默認值。 我不確定問題出在Spring Security的配置中還是用戶和權限的映射問題。 這是配置,控制器-> UserDetailsService->存儲庫,實體和sql表。
Spring Secutrity配置
@Configuration
@EnableWebSecurity
@ComponentScan("com.mithrandir.springcrud")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private AuthenticationSuccessHandlerImpl successHandler;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider
= new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").hasRole("USER")
.antMatchers("/add/**").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/updateProduct/**").hasRole("ADMIN")
.antMatchers("/delete/**").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/doLogin")
.successHandler(successHandler)
.successForwardUrl("/login")
.usernameParameter("email")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.logoutUrl("/doLogout")
.logoutSuccessUrl("/logout")
.permitAll()
.and()
.exceptionHandling().accessDeniedPage("/accessDenied");
}
}
LoginController登錄POST請求
@PostMapping("/login")
public String postLogin(Model model, HttpSession session) {
Authentication authentication = SecurityContextHolder
.getContext().getAuthentication();
validatePrinciple(authentication.getPrincipal());
User loggedInUser = ((UserPrincipal) authentication.getPrincipal()).getUser();
model.addAttribute("currentUserId", loggedInUser.getId());
model.addAttribute("currentUser", loggedInUser.getEmail());
session.setAttribute("userId", loggedInUser.getId());
return "redirect:/";
}
private void validatePrinciple(Object principal) {
if (!(principal instanceof UserPrincipal)) {
throw new IllegalArgumentException("Principal can not be null!");
}
}
AuthenticationSuccessHandler更新最后在線日期
@Component
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
@Autowired
UserRepository userRepository;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
userRepository.updateLastLogin(new Date());
}
}
UserDetailsService
@Service
@Transactional
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
System.out.println("UserDetailsService, user from db: " + user);
if (user == null) {
throw new UsernameNotFoundException("No user found with email: " + email);
}
return new UserPrincipal(user);
}
}
用戶資料庫
@Override
public User findByEmail(String email) {
Session session = sessionFactory.getCurrentSession();
Query<User> query = session.createQuery("FROM User u WHERE u.email=:email", User.class);
query.setParameter("email", email);
System.out.println("UserDAO: finding user by email");
return query.uniqueResult();
}
用戶主體:
public class UserPrincipal implements UserDetails {
/**
*
*/
private static final long serialVersionUID = 777221850757925972L;
private final User user;
public UserPrincipal(User user) {
this.user = user;
}
@Override
public String getUsername() {
return user.getEmail();
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getAuthority().toString()))
.collect(Collectors.toSet());
}
// getters and setters through the user field
}
用戶實體:
@Entity
@Table(name = "user")
public class User implements Serializable {
/**
*
*/
private static final long serialVersionUID = 7630269547387051923L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "firstname")
private String firstName;
@Column(name = "lastname")
private String lastName;
@Column(name = "email", unique = true)
private String email;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "enabled", nullable = false)
private boolean enabled;
@Column(name = "accountNonExpired", nullable = false)
private boolean accountNonExpired;
@Column(name = "credentialsNonExpired", nullable = false)
private boolean credentialsNonExpired;
@Column(name = "accountNonLocked", nullable = false)
private boolean accountNonLocked;
@Column(name = "creationDateTime")
private Date createDateTime;
@Column(name = "updatedDateTime")
private Date updateDateTime;
@Column(name = "lastOnlineDateTime", nullable = false)
private Date lastOnline;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(name = "user_authorities",
joinColumns = { @JoinColumn(name = "user_id") },
inverseJoinColumns = { @JoinColumn(name = "authority_id") })
@Fetch(value=FetchMode.SELECT)
private Set<Authorities> authorities = new HashSet<>();
public User() {
}
//... getters and setters
單位實體:
@Entity
@Table(name = "authorities")
public class Authorities implements Serializable {
/**
*
*/
private static final long serialVersionUID = 5383739733021126813L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
private AuthorityType authority;
public Authorities(AuthorityType authority) {
this.authority = authority;
}
//... getters and setters
AuthorityType枚舉:
public enum AuthorityType {
ROLE_ADMIN,
ROLE_MANAGER,
ROLE_USER
}
最后是sql模式:
CREATE TABLE `user` (
`id` BIGINT(19) NOT NULL AUTO_INCREMENT,
`firstName` VARCHAR(30) NOT NULL,
`lastName` VARCHAR(30) NOT NULL,
`email` VARCHAR(50) NOT NULL,
`password` CHAR(76) NOT NULL,
`enabled` TINYINT NOT NULL,
`accountNonExpired` TINYINT NOT NULL,
`credentialsNonExpired` TINYINT NOT NULL,
`accountNonLocked` TINYINT NOT NULL,
`creationDateTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedDateTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`lastOnlineDateTime` TIMESTAMP NOT NULL,
CONSTRAINT user_pk
PRIMARY KEY (`id`)
);
CREATE TABLE `authorities` (
`id` BIGINT(19) NOT NULL auto_increment,
`authority` varchar(50) NOT NULL,
PRIMARY KEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `user_authorities` (
`user_id` BIGINT(19) NOT NULL,
`authority_id` BIGINT(19) NOT NULL,
KEY `user` (`user_id`),
KEY `authority` (`authority_id`),
CONSTRAINT `user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `authority` FOREIGN KEY (`authority_id`) REFERENCES `authorities` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
這是我第一次輸入正確的憑據時從控制台獲得的輸出:
UserDAO: finding user by email
Hibernate: select user0_.id as id1_3_, user0_.accountNonExpired as accountN2_3_, user0_.accountNonLocked as accountN3_3_, user0_.creationDateTime as creation4_3_, user0_.credentialsNonExpired as credenti5_3_, user0_.email as email6_3_, user0_.enabled as enabled7_3_, user0_.firstname as firstnam8_3_, user0_.lastname as lastname9_3_, user0_.lastOnlineDateTime as lastOnl10_3_, user0_.password as passwor11_3_, user0_.updatedDateTime as updated12_3_ from user user0_ where user0_.email=?
Hibernate: select authoritie0_.user_id as user_id1_4_0_, authoritie0_.authority_id as authorit2_4_0_, authoritie1_.id as id1_0_1_, authoritie1_.authority as authorit2_0_1_ from user_authorities authoritie0_ inner join authorities authoritie1_ on authoritie0_.authority_id=authoritie1_.id where authoritie0_.user_id=?
heinäkuuta 10, 2019 5:10:40 IP. org.hibernate.engine.loading.internal.LoadContexts cleanup
WARN: HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@67b156c6<rs=com.mchange.v2.c3p0.impl.NewProxyResultSet@3fe5ce85 [wrapping: null]>
heinäkuuta 10, 2019 5:10:40 IP. org.hibernate.engine.loading.internal.CollectionLoadContext cleanup
WARN: HHH000160: On CollectionLoadContext#cleanup, localLoadingCollectionKeys contained [1] entries
嘗試使用無效的憑據登錄時:
UserDAO: finding user by email
Hibernate: select user0_.id as id1_3_, user0_.accountNonExpired as accountN2_3_, user0_.accountNonLocked as accountN3_3_, user0_.creationDateTime as creation4_3_, user0_.credentialsNonExpired as credenti5_3_, user0_.email as email6_3_, user0_.enabled as enabled7_3_, user0_.firstname as firstnam8_3_, user0_.lastname as lastname9_3_, user0_.lastOnlineDateTime as lastOnl10_3_, user0_.password as passwor11_3_, user0_.updatedDateTime as updated12_3_ from user user0_ where user0_.email=?
UserDetailsService, user from db: null
因此,我可以看到存儲庫可以從數據庫中找到用戶,但是由於某種原因無法將其拔出...映射錯誤還是配置錯誤? 預先感謝您抽出寶貴的時間來幫助您!
我已經建立了完整的Java配置而不需要任何xml的內容。
1.)我猜您的安全配置不正確。 不要使用@ComponentScan批注,因為此類既不是您的根onfiguration類也不是您的servlet配置。 只需刪除此行。
2.)您必須告訴反仇者做什么。 像這樣:
.antMatchers("/administration/**").access("hasRole('ADMIN')")
因為您想授予對此URL的訪問權限。
3.)我不確定,但是如果憑據可以,您可以檢查是否真的向用戶授予了權限嗎? 我正在使用spring並且在UserDetailsService中有類似的內容:
if (user != null) {
Collection<GrantedAuthority> authorites = new ArrayList<GrantedAuthority>();
for (Iterator<UserUserroles> i = user.getRoles().iterator(); i.hasNext();) {
UserRole role = i.next().getRoleid();
authorites.add(new SimpleGrantedAuthority(role.getName()));
}
SpringSecurityUser securityUser;
securityUser = new SpringSecurityUser(user, authorites);
return securityUser;
}
throw new UsernameNotFoundException(username);
我手動將用戶的角色添加為授權。 也許您的解決方案更聰明,但是請檢查這部分是否正常工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.