上章回顧:
上一章中,我們將用戶名、密碼以及用戶對應的角色都配置於applicationContext-security.xml中,基本實現了我們能控制用戶的訪問權限。但是在現實開發中,我們不可能將用戶信息硬編碼在配置文件中,通常我們都是存放到數據中。同時我們應該對用戶的密碼進行加密存儲。
目標:
1.將用戶信息存放於數據庫
2.對用戶的密碼進行加密
詳細操作:
1.其他代碼參考上一章中代碼。本章中,首先我們要創建一張數據表來記錄我們的用戶信息。SpringSecurity提供的驗證機制中,首先我們自定義的Entity類要實現UserDetails,並且要實現他接口中的幾個方法,下面來看具體代碼:
@SuppressWarnings("serial") @Entity(name = "User") @Table(name = "userinfo") public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", unique = true, nullable = false) private int id; @Column(name = "username") private String username; @Column(name = "password") private String password; @Column(name = "role") private String role; @Column(name = "enabled") private boolean enabled = false; @Column(name = "salt_value") private String salt_value; public int getId() { return id; } public void setId(int id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<GrantedAuthority> list = new ArrayList<GrantedAuthority>(); System.out.println("User's role: " + getRole()); list.add(new SimpleGrantedAuthority(getRole())); return list; } @Override public String getPassword() { return this.password; } @Override public String getUsername() { // TODO Auto-generated method stub return this.username; } @Override public boolean isAccountNonExpired() { // TODO 根據需要修改 return true; } @Override public boolean isAccountNonLocked() { // TODO Auto-generated method stub return true; } @Override public boolean isCredentialsNonExpired() { // TODO Auto-generated method stub return true; } @Override public boolean isEnabled() { // TODO Auto-generated method stub return this.enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public String getSalt_value() { return salt_value; } public void setSalt_value(String salt_value) { this.salt_value = salt_value; } }
首先,getAuthorities()方法,我們需要返回用戶的角色集合,但是由於我們爲了方便,一個用戶就只定義了一個角色。實際開發中,我們可以換成List,然後再來添加到對應的集合中。
剩下幾個@Override的方法,都是接口中需要我們必須實現的方法,因爲在SpringSecurity去判斷用戶是否能登錄,是否有權限去訪問某些資源的時候就是通過這幾個屬性去判斷的,所以我們根據自己的需求來進行返回值。而且每個方法名稱已經寫的明確代表的意思。
2.Entity建立好之後,我們再來建立我們的數據。這裏直接貼出代碼:
DROP TABLE IF EXISTS `userinfo`; CREATE TABLE `userinfo` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `role` varchar(20) DEFAULT NULL, `enabled` tinyint(1) DEFAULT NULL, `salt_value` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3.Spring Security如何來驗證我們的用戶呢?按照我們自己的思路來看,應該是:查找用戶是否在我們的數據庫中->驗證用戶密碼->判斷用戶是否可用->其他判斷->用戶是否登錄成功。在此本實例中,我們已經在SpringSecurity配置文件中配置了訪問資源的權限,因此在此我們只需用找到該用戶,然後返回給SpringSecurity的處理器,讓他自己來處理。所以我們要實現SpringSecurity提供的UserDetailsService接口:
public interface UserService extends UserDetailsService{ } @Service(value = "userService") public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { System.out.println("find the user name: " + username); User user = userDao.getUserByUserName(username); System.out.println(user == null); if (user == null) { System.out.println("username can't found"); throw new UsernameNotFoundException("Username not found..."); } return user; } }
4.到此,基本已經搞定。現在我們只需用修改配置文件,告訴SpringSecurity按照我們自己定義的方法來實現,修改applicationContext-security.xml:
<authentication-manager> <authentication-provider user-service-ref="userService"> <password-encoder ref="passwordEncoder"> <salt-source ref="saltSource"/> </password-encoder> </authentication-provider> </authentication-manager> <!-- 密碼鹽值,取用戶名作爲鹽值 --> <!-- userPropertyToUse:主要用來將我們的對象放入到他的方法中,根據這個屬性名稱取出鹽值 --> <beans:bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource"> <beans:property name="userPropertyToUse" value="salt_value"></beans:property> </beans:bean> <!-- SHA加密類 --> <beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"> </beans:bean>
我們通過使用SpringSecurity提供的加密方法進行加密,同時我們增加鹽值在密碼中,這樣讓破解失效。
authentication-provider中user-service-ref,就是我們上一點中自定義的service類。同時我們提供加密方式。
5.在此我們有一個問題,如果我們手工的加入數據到數據庫中,密碼是明文,沒有進行加密的。爲了我們能夠順利登陸,我們設計一個註冊用戶的頁面,用來註冊用戶。註冊用戶頁面我們就自己發揮了,主要傳遞到後臺的數據包括:用戶名、密碼、角色。此點主要是看看如何對密碼進行加密:
/** * 注入兩個工具類 */ @Resource private ReflectionSaltSource saltSource; @Resource private ShaPasswordEncoder passwordEncoder; /** * 註冊用戶 * @param user * @return */ @RequestMapping("/register") public ModelAndView register(User user) { user.setEnabled(true); // 使用系統當前時間作爲鹽值 user.setSalt_value(System.currentTimeMillis() + ""); // 加密密碼:原有密碼+鹽值。鹽值通過從user對象中獲取,我們在配置中有寫到查找哪個屬性 String password = passwordEncoder.encodePassword(user.getPassword(), saltSource.getSalt(user)); user.setPassword(password); if (userDao.addUser(user)) { System.out.println("register success"); } else { System.out.println("failed"); } return new ModelAndView("redirect:/login.jsp"); }
6.好了,用戶我們註冊成功,可以直接跳轉到登錄頁面,用剛纔註冊的用戶名和密碼進行登錄,並進行權限測試。