SpringSecurity給我們提供了一套最基本的認證方式,可是這種方式遠遠不能滿足大多數系統的需求。不過好在SpringSecurity給我們預留了許多可擴展的接口給我們,我們可以基於這些接口實現自己的認證方式。
一、前期准備工作
1.1、創建示例數據庫
Student表:
create table student ( id int auto_increment primary key, stuName varchar(8) null, password varchar(16) null, joinTime datetime null, clz_id int null ) ;
Classes(班級)表:
create table classes ( id int auto_increment primary key, clz_name varchar(16) not null ) ;
1.2、添加相關依賴
compile group: 'mysql', name: 'mysql-connector-java' compile group: 'org.springframework.security', name: 'spring-security-taglibs' compile('org.springframework.boot:spring-boot-starter-jdbc')
二、實現步驟
2.1 定義Student類

package com.bdqn.lyrk.security.study.app.pojo; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import java.sql.Timestamp; import java.util.Collection; public class Student extends User { private Timestamp joinTime; public Timestamp getJoinTime() { return joinTime; } public void setJoinTime(Timestamp joinTime) { this.joinTime = joinTime; } public Student(String username, String password, Collection<? extends GrantedAuthority> authorities) { super(username, password, authorities); } }
在這里定義的類繼承User,User是SpringSecurity里的一個類,用以描述一個用戶和其最基本的屬性,當然我們要擴展它的用戶我們也可以實現UserDetails接口
2.2 實現UserDetailsService

package com.bdqn.lyrk.security.study.app.service; import com.bdqn.lyrk.security.study.app.pojo.Student; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; 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.stereotype.Service; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.Map; @Service public class UserService implements UserDetailsService { @Autowired private JdbcTemplate jdbcTemplate; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User.UserBuilder users = User.withDefaultPasswordEncoder(); Map<String, Object> map = jdbcTemplate.queryForMap("select t.clz_name,t1.stuName,t1.password,t1.joinTime from student t1 inner join classes t on t.id = t1.clz_id where stuName = ?", username); Timestamp joinTime = null; if (map != null && map.size() > 0) { String stuName = (String) map.get("stuName"); String password = (String) map.get("password"); joinTime = (Timestamp) map.get("joinTime"); String clzName = (String) map.get("clz_name"); users.password(password); users.username(stuName); SimpleGrantedAuthority authority = new SimpleGrantedAuthority(clzName); List<GrantedAuthority> list = new ArrayList<>(); list.add(authority); users.authorities(list); } UserDetails userDetails = users.build(); Student student = new Student(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities()); // UserDetails userDetails = User.withDefaultPasswordEncoder(). student.setJoinTime(joinTime); return student; } }
在這個接口里我們要實現根據用戶名查找用戶的方法,那么一般情況下我們都會根據自己系統的用戶表來獲取用戶信息,這里面注意幾個方面:
1)需要設置PasswordEncoder
2) 需要設置其角色信息,那么在這里我用班級來表示用戶的角色
3)用戶的三個重要屬性就是 用戶名,密碼與權限
4) 這里的返回值(UserDetails)不能返回null,如果根據用戶名找不到對應的用戶可以拋出UsernameNotFoundException異常
2.3 改造WebSecurityConfig

package com.bdqn.lyrk.security.study.app.config; import com.bdqn.lyrk.security.study.app.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** * spring-security的相關配置 * * @author chen.nie * @date 2018/6/7 **/ @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Override protected void configure(HttpSecurity http) throws Exception { /* 1.配置靜態資源不進行授權驗證 2.登錄地址及跳轉過后的成功頁不需要驗證 3.其余均進行授權驗證 */ http. authorizeRequests().antMatchers("/static/**").permitAll(). and().authorizeRequests().antMatchers("/user/**").hasRole("7022"). and().authorizeRequests().anyRequest().authenticated(). and().formLogin().loginPage("/login").successForwardUrl("/toIndex").permitAll() .and().logout().logoutUrl("/logout").invalidateHttpSession(true).deleteCookies().permitAll() ; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //設置自定義userService auth.userDetailsService(userService); } }
在這里主要做以下處理:
1)針對於/user/**路徑的請求需要設置對應的權限
2) 做用戶注銷的處理,用戶注銷時需要銷毀session與cookie
3)配置自定義UserDetailService
2.4、改造index.jsp
<%-- Created by IntelliJ IDEA. User: chen.nie Date: 2018/6/8 Time: 上午9:56 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <html> <head> <title>Title</title> </head> <body> 歡迎:${user.username} <sec:authorize access="hasRole('7022')"> 加入時間:${user.joinTime} </sec:authorize> <form action="/logout" method="post"> <input type="submit" value="退出" /> <sec:csrfInput/> </form> </body> </html>
在這里面我們使用spring對security標簽的支持判斷當前用戶是否有對應的角色,另外我們在處理登出操作時必須為post提交且有對應的token防止csrf