系列博文html
項目已上傳至guthub 傳送門java
JavaWeb-SpringSecurity初認識 傳送門mysql
JavaWeb-SpringSecurity在數據庫中查詢登錄用戶 傳送門git
JavaWeb-SpringSecurity自定義登錄頁面 傳送門github
JavaWeb-SpringSecurity實現需求-判斷請求是否以html結尾 傳送門web
JavaWeb-SpringSecurity自定義登錄配置 傳送門spring
JavaWeb-SpringSecurity圖片驗證ImageCode 傳送門sql
JavaWeb-SpringSecurity記住我功能 傳送門數據庫
JavaWeb-SpringSecurity使用短信驗證碼登錄 傳送門安全
在MySQL數據庫中建立springsecurity數據庫
(id、username、password都是根據User.java映射過來的)
在application.properties中編寫配置文件
#datasource spring.datasource.url=jdbc:mysql:///springsecurity?serverTimezone=UTC&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.dricer-class-name=com.mysql.jdbc.Driver #jpa #打印出數據庫語句 spring.jpa.show-sql=true #更新數據庫表 spring.jpa.hibernate.ddl-auto=update
建立domain實體層User.java和repository存儲層接口UserRepository.java
書寫用戶User.java實體
//用戶是否沒有失效 @Transient private boolean accountNonExpried; //用戶是否凍結 @Transient private boolean accountNonLocked; //證實是否過時 @Transient private boolean credentialsNonExpired; //判斷是否刪除 @Transient private boolean enabled; @Transient //添加 @Transient 註解能夠不將 Set<GrantedAuthority>映射到數據庫表上 private Set<GrantedAuthority> authorities; //給hibernatre用的構造方法 protected User() { } public User(Long id,String username,String password) { this.id = id; this.username = username; this.password = password; } //給SpringSecurity用的構造方法 public User(String username,String password, Collection<? extends GrantedAuthority> authorities) { this(username,password,true,true,true,true,authorities); } public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) { if (((username == null) || "".equals(username)) || (password == null)) { throw new IllegalArgumentException( "Cannot pass null or empty values to constructor"); } this.username = username; this.password = password; this.enabled = enabled; this.accountNonExpried = accountNonExpired; this.credentialsNonExpired = credentialsNonExpired; this.accountNonLocked = accountNonLocked; this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities)); } private static SortedSet<GrantedAuthority> sortAuthorities( Collection<? extends GrantedAuthority> authorities) { Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection"); // Ensure array iteration order is predictable (as per // UserDetails.getAuthorities() contract and SEC-717) SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<>( new AuthorityComparator()); for (GrantedAuthority grantedAuthority : authorities) { Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements"); sortedAuthorities.add(grantedAuthority); } return sortedAuthorities; } private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; public int compare(GrantedAuthority g1, GrantedAuthority g2) { // Neither should ever be null as each entry is checked before adding it to // the set. // If the authority is null, it is a custom authority and should precede // others. if (g2.getAuthority() == null) { return -1; } if (g1.getAuthority() == null) { return 1; } return g1.getAuthority().compareTo(g2.getAuthority()); } }
package com.Gary.GaryRESTful.domain; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Transient; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.SpringSecurityCoreVersion; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.util.Assert; @Entity public class User implements UserDetails{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; //用戶是否沒有失效 @Transient private boolean accountNonExpried; //用戶是否凍結 @Transient private boolean accountNonLocked; //證實是否過時 @Transient private boolean credentialsNonExpired; //判斷是否刪除 @Transient private boolean enabled; @Transient //添加 @Transient 註解能夠不將 Set<GrantedAuthority>映射到數據庫表上 private Set<GrantedAuthority> authorities; //給hibernatre用的構造方法 protected User() { } public User(Long id,String username,String password) { this.id = id; this.username = username; this.password = password; } //給SpringSecurity用的構造方法 public User(String username,String password, Collection<? extends GrantedAuthority> authorities) { this(username,password,true,true,true,true,authorities); } public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) { if (((username == null) || "".equals(username)) || (password == null)) { throw new IllegalArgumentException( "Cannot pass null or empty values to constructor"); } this.username = username; this.password = password; this.enabled = enabled; this.accountNonExpried = accountNonExpired; this.credentialsNonExpired = credentialsNonExpired; this.accountNonLocked = accountNonLocked; this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities)); } private static SortedSet<GrantedAuthority> sortAuthorities( Collection<? extends GrantedAuthority> authorities) { Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection"); // Ensure array iteration order is predictable (as per // UserDetails.getAuthorities() contract and SEC-717) SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<>( new AuthorityComparator()); for (GrantedAuthority grantedAuthority : authorities) { Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements"); sortedAuthorities.add(grantedAuthority); } return sortedAuthorities; } private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; public int compare(GrantedAuthority g1, GrantedAuthority g2) { // Neither should ever be null as each entry is checked before adding it to // the set. // If the authority is null, it is a custom authority and should precede // others. if (g2.getAuthority() == null) { return -1; } if (g1.getAuthority() == null) { return 1; } return g1.getAuthority().compareTo(g2.getAuthority()); } } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } //用戶權限 @Override public Collection<? extends GrantedAuthority> getAuthorities() { // TODO Auto-generated method stub return authorities; } //用戶是否沒有失效 @Override public boolean isAccountNonExpired() { // TODO Auto-generated method stub return accountNonExpried; } //用戶是否被凍結 @Override public boolean isAccountNonLocked() { // TODO Auto-generated method stub return accountNonLocked; } //用戶是否證實權限過時 @Override public boolean isCredentialsNonExpired() { // TODO Auto-generated method stub return credentialsNonExpired; } //判斷用戶是否刪除 @Override public boolean isEnabled() { // TODO Auto-generated method stub return enabled; } }
完善User.java實體
public User(Long id,String username,String password) { this.id = id; this.username = username; this.password = password; } //給SpringSecurity用的構造方法 public User(String username,String password, Collection<? extends GrantedAuthority> authorities) { this(username,password,true,true,true,true,authorities); } public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) { if (((username == null) || "".equals(username)) || (password == null)) { throw new IllegalArgumentException( "Cannot pass null or empty values to constructor"); } this.username = username; this.password = password; this.enabled = enabled; this.accountNonExpried = accountNonExpired; this.credentialsNonExpired = credentialsNonExpired; this.accountNonLocked = accountNonLocked; this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities)); } private static SortedSet<GrantedAuthority> sortAuthorities( Collection<? extends GrantedAuthority> authorities) { Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection"); // Ensure array iteration order is predictable (as per // UserDetails.getAuthorities() contract and SEC-717) SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<>( new AuthorityComparator()); for (GrantedAuthority grantedAuthority : authorities) { Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements"); sortedAuthorities.add(grantedAuthority); } return sortedAuthorities; } private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; public int compare(GrantedAuthority g1, GrantedAuthority g2) { // Neither should ever be null as each entry is checked before adding it to // the set. // If the authority is null, it is a custom authority and should precede // others. if (g2.getAuthority() == null) { return -1; } if (g1.getAuthority() == null) { return 1; } return g1.getAuthority().compareTo(g2.getAuthority()); } }
package com.Gary.GaryRESTful.domain; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.SpringSecurityCoreVersion; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.util.Assert; @Entity public class User implements UserDetails{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; //用戶是否沒有失效 private boolean accountNonExpried; //用戶是否凍結 private boolean accountNonLocked; //證實是否過時 private boolean credentialsNonExpired; //判斷是否刪除 private boolean enabled; private Set<GrantedAuthority> authorities; //給hibernatre用的構造方法 protected User() { } public User(Long id,String username,String password) { this.id = id; this.username = username; this.password = password; } //給SpringSecurity用的構造方法 public User(String username,String password, Collection<? extends GrantedAuthority> authorities) { this(username,password,true,true,true,true,authorities); } public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) { if (((username == null) || "".equals(username)) || (password == null)) { throw new IllegalArgumentException( "Cannot pass null or empty values to constructor"); } this.username = username; this.password = password; this.enabled = enabled; this.accountNonExpried = accountNonExpired; this.credentialsNonExpired = credentialsNonExpired; this.accountNonLocked = accountNonLocked; this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities)); } private static SortedSet<GrantedAuthority> sortAuthorities( Collection<? extends GrantedAuthority> authorities) { Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection"); // Ensure array iteration order is predictable (as per // UserDetails.getAuthorities() contract and SEC-717) SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<>( new AuthorityComparator()); for (GrantedAuthority grantedAuthority : authorities) { Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements"); sortedAuthorities.add(grantedAuthority); } return sortedAuthorities; } private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; public int compare(GrantedAuthority g1, GrantedAuthority g2) { // Neither should ever be null as each entry is checked before adding it to // the set. // If the authority is null, it is a custom authority and should precede // others. if (g2.getAuthority() == null) { return -1; } if (g1.getAuthority() == null) { return 1; } return g1.getAuthority().compareTo(g2.getAuthority()); } } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } //用戶權限 @Override public Collection<? extends GrantedAuthority> getAuthorities() { // TODO Auto-generated method stub return authorities; } //用戶是否沒有失效 @Override public boolean isAccountNonExpired() { // TODO Auto-generated method stub return accountNonExpried; } //用戶是否被凍結 @Override public boolean isAccountNonLocked() { // TODO Auto-generated method stub return accountNonLocked; } //用戶是否證實權限過時 @Override public boolean isCredentialsNonExpired() { // TODO Auto-generated method stub return credentialsNonExpired; } //判斷用戶是否刪除 @Override public boolean isEnabled() { // TODO Auto-generated method stub return enabled; } }
完善UserRepository.java接口,實現查找用戶姓名方法
@Query(value = "select * from user where username = ?1",nativeQuery = true) User findUserByUsername(String username);
在UserService.java中實現查找用戶Service
@Autowired private PasswordEncoder passwordEncoder; @Autowired private UserRepository userRepository; //spring security默認處理登錄(username爲輸入的username) @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO Auto-generated method stub //System.out.println(username); User user = userRepository.findUserByUsername(username); //用戶名,密碼,權限 //User實現UserDetails接口 return new User(username,passwordEncoder.encode(user.getPassword()),AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); }
測試時發現,當用戶輸入錯誤的用戶名和密碼時,控制檯會報空指針異常,前臺爲給出「壞的憑證」給用戶提示信息。
緣由:springsecurity會攔截一切其它的請求。只有當用戶輸入正確的用戶名和密碼,springsecurity纔會釋放用戶正常的請求。
package com.Gary.GaryRESTful.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; //Web應用安全適配器 @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ //告訴SpringSecurity密碼用什麼加密的 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } //表單驗證(身份認證) protected void configure(HttpSecurity http) throws Exception{ http.formLogin() .and() //請求受權 .authorizeRequests() //全部請求都被攔截,跳轉到(/login請求中) .anyRequest() //都須要咱們身份認證 .authenticated(); } }
package com.Gary.GaryRESTful.repository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import com.Gary.GaryRESTful.domain.User; public interface UserRepository extends CrudRepository<User,Long>{ @Query(value = "select * from user where username = ?1",nativeQuery = true) User findUserByUsername(String username); }
package com.Gary.GaryRESTful.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.AuthorityUtils; //import org.springframework.security.core.userdetails.User; 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.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import com.Gary.GaryRESTful.domain.User; import com.Gary.GaryRESTful.repository.UserRepository; //用SprinSecurity默認的登錄系統 //UserService要實現UserDetailsService接口 @Component public class UserService implements UserDetailsService{ @Autowired private PasswordEncoder passwordEncoder; @Autowired private UserRepository userRepository; //spring security默認處理登錄(username爲輸入的username) @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO Auto-generated method stub //System.out.println(username); User user = userRepository.findUserByUsername(username); //用戶名,密碼,權限 //User實現UserDetails接口 return new User(username,passwordEncoder.encode(user.getPassword()),AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); } }
因此咱們能夠在UserDetails.java中進行修改,添加判斷,若是在數據庫中未查詢到用戶時返回null
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO Auto-generated method stub //System.out.println(username); User user = userRepository.findUserByUsername(username); //用戶名,密碼,權限 if(user == null) { throw new UsernameNotFoundException(username); } //User實現UserDetails接口 return new User(username,passwordEncoder.encode(user.getPassword()),AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); }
package com.Gary.GaryRESTful.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.AuthorityUtils; //import org.springframework.security.core.userdetails.User; 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.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import com.Gary.GaryRESTful.domain.User; import com.Gary.GaryRESTful.repository.UserRepository; //用SprinSecurity默認的登錄系統 //UserService要實現UserDetailsService接口 @Component public class UserService implements UserDetailsService{ @Autowired private PasswordEncoder passwordEncoder; @Autowired private UserRepository userRepository; //spring security默認處理登錄(username爲輸入的username) @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO Auto-generated method stub //System.out.println(username); User user = userRepository.findUserByUsername(username); //用戶名,密碼,權限 if(user == null) { throw new UsernameNotFoundException(username); } //User實現UserDetails接口 return new User(username,passwordEncoder.encode(user.getPassword()),AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); } }
拓展:根據SpringSecurity提供的User方法
//給SpringSecurity用的構造方法 public User(String username,String password, Collection<? extends GrantedAuthority> authorities) { this(username,password,true,true,true,true,authorities); }
能夠在UserService.java中提供的UserDetails返回值中添加4個boolean值
return new User(user.getUsername(),passwordEncoder.encode(user.getPassword()),true,true,true,true,AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
這四個boolean值表明着
//用戶是否沒有失效 @Transient private boolean accountNonExpried; //用戶是否凍結 @Transient private boolean accountNonLocked; //證實是否過時 @Transient private boolean credentialsNonExpired; //判斷是否刪除 @Transient private boolean enabled;
若是return返回值中四個參數都爲true,springscurity不會去進行攔截驗證,
當其中一個參數爲false時,springsecurity就會對用戶進行攔截驗證~