javascript
Spring Security | 轻松搞定认证授权~
文章目錄
- 一、Spring Security 簡介
- 二、整合 Spring Security
- 1、創建項目,導入依賴
- 2、創建數據庫,搭建環境
- 3、配置數據庫
- 4、創建實體類
- 5、實現 UserMapper
- 6、實現 UserService
- 7、實現 Controller
- 8、配置 Spring Security
- 9、攔截登錄認證測試
一、Spring Security 簡介
通常,一個正式的項目都會具有安全方面的需求。顯然,手動去實現權限管理等功能是較為麻煩的。因此,我們們往往需要借助一些安全框架來實現,而 Spring Security 就是常見的安全框架之一。具體而言,Spring Security 可以幫助我們自動完成如下功能:
- 請求攔截
- 登錄認證
- 角色授權
- 賬戶注銷
- 自動登錄
- 其他功能 …
其中,作為一個安全框架,最核心的自然還是認證以及授權功能,我們只需提供相關的信息,剩下的大部分工作都可以自動實現,這也是其強大的地方。同時,作為 Spring 家族的一部分,Spring Boot 也能夠很好地將其整合,只需要導入一個依賴并添加一個配置類即可。
以下將結合數據庫,在 Spring Boot 項目中整合 Spring Security,并實現基本的登錄認證、權限管理等功能。
二、整合 Spring Security
1、創建項目,導入依賴
創建一個 Spring Boot 項目(默認添加 web 依賴)
項目結構如下:
然后在 pom.xml 中導入如下依賴:
- spring-boot-starter-security:整合 Spring Security
- themeleaf:視圖跳轉需要用到
- jdbc, mysql, mybatis:連接數據庫
- lombok:通過注解生成實體類的 getter/setter,提高效率
2、創建數據庫,搭建環境
一共有如下 3 張表:
- user:用戶表
- role:角色(權限)表
- user_role:多對多關聯表
創建數據庫 springboot-data,建表的 SQL 如下:
3、配置數據庫
在 application.yaml 中配置數據庫連接相關參數:
spring:datasource:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/springboot-data?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.cj.jdbc.Drivermybatis:type-aliases-package: com.study.pojomapper-locations: classpath:mapper/*.xml4、創建實體類
創建 Role 實體類
@Data @AllArgsConstructor @NoArgsConstructor public class Role {private Integer role_id;private String role_name;private String role_detail; }通過 lombok 提供的 @Data注解 自動生成 getter/setter;通過 @AllArgsConstructor 注解與 @NoArgsConstructor 注解分別生成有參構造以及無參構造
創建 User 實體類,實現 UserDetails 接口,重寫其中的方法,在該類中用一個 List 集合存放該用戶所有的角色
由于密碼認證工作都是由 Spring Security 自動完成的,因此我們在這里只是簡單地返回用戶的各種信息即可,如 getPassword() 返回用戶的密碼,如果用戶登錄失敗,Spring Security 會自動拋出相關異常。
- isAccountNonExpired:賬戶是否未到期
- isAccountNonLocked:賬戶是否未被鎖定
- isCredentialsNonExpired:賬戶密碼是否未到期
- isEnabled:賬戶是否可用
5、實現 UserMapper
UserMapper 接口中定義如下兩個方法:
- queryUserByName :根據用戶名查詢用戶
- queryRolesByUid:根據用戶 id 查詢角色
對應的 mapper.xml:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.study.mapper.UserMapper"><select id="queryUserByName" resultType="user">select * from user where name = #{name};</select><select id="queryRolesByUid" resultType="role">select *from role r, user_role urwhere ur.role_id = r.role_id and ur.user_id = #{id};</select></mapper>6、實現 UserService
創建 UserService,實現 UserDetailsService 接口,Spring Security 在進行登錄認證時,會執行其中的 loadUserByUsername 方法,傳入的參數為用戶表單中輸入用戶名,我們通過該用戶名在數據庫中查找對應的 user,如果找不到,則拋出異常,如果用戶存在,則根據其 id 找出所有角色,并將其賦值給當前的 user:
@Service public class UserService implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {User user = userMapper.queryUserByName(name);if (user == null) {throw new UsernameNotFoundException("賬戶不存在!");}user.setRoles(userMapper.queryRolesByUid(user.getId()));return user;}}7、實現 Controller
@Controller public class RouterController {@RequestMapping({"/", "/index"})public String index() {return "index";}@RequestMapping("/toLogin")public String toLogin() {return "login";}@GetMapping("/sadmin/search")@ResponseBodypublic String showSadminData() {return "授權通過,擁有超級管理員權限!";}@GetMapping("/admin/search")@ResponseBodypublic String showAdminData() {return "授權通過,擁有管理員權限!";}@GetMapping("/user/search")@ResponseBodypublic String showUserData() {return "授權通過,擁有普通用戶權限!";}}- / 或 /index 跳轉到首頁,后續需在 Spring Security 配置類中放行
- /toLogin 請求跳轉到自定義的登錄頁面 login.html,后續需在 Spring Security 配置類中指定其為 loginPage
- 其他三個請求 分別表示三種角色權限可以訪問的請求
8、配置 Spring Security
自定義一個類繼承自 WebSecurityConfigurerAdapter,同時添加 Configuration 注解:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {}- 重寫其中的 configure(AuthenticationManagerBuilder auth) 方法,將 userService 傳入其中
- 重寫 configure(WebSecurity web) 方法,其中將靜態資源路徑放行,當然也可通過 HttpSecurity .authorizeRequests().antMatchers("xxx").permitAll() 實現
- passwordEncoder 中返回 NoOpPasswordEncoder.getInstance(),即不采取加密
接下來就是 Spring Security 的核心配置了:
- .antMatchers("/").permitAll() 表示將 "/" 請求放行,所有人都能訪問
- antMatchers("/sadmin/**").hasRole("sadmin")表示攔截以 /sadmin 開頭的所有請求,只有當用戶具備 sadmin 角色時才放行,下面的兩行依次類推
- .anyRequest().authenticated() 表示所有的請求都需登錄認證后才可訪問
- formLogin() 表示開啟表單登錄;loginPage 為自定義跳轉的登錄頁面(如不設置,則進入到 Spring Securtity 提供的默認登錄界面)
- loginProcessingUrl 表示處理登錄的請求名稱,表單的登錄請求對應此 url
- usernameParameter("name").passwordParameter("pwd") 為登錄表單中 input 的自定義的 name 名稱,如不設置,默認為 username, password
- successHandler 添加登錄成功的回調函數,failureHandler 添加登錄失敗的回調函數,一般在其中返回一些提示信息
- logout 開啟注銷功能,即當我們訪問 /logout 請求時,自動完成注銷操作
- rememberMe 開啟記住我功能,當勾選記住我登錄時,會創建一個 Cookie,rememberMeParameter 為表單控件中單選框的 name 名稱,類似于上面的 usernameParameter
以下是登錄成功的回調函數,這里將用戶信息以 JSON 形式返回:
public AuthenticationSuccessHandler successHandler() {return new AuthenticationSuccessHandler() {@Overridepublic void onAuthenticationSuccess(HttpServletRequest req,HttpServletResponse resp,Authentication authentication)throws IOException, ServletException {Object principal = authentication.getPrincipal();resp.setContentType("application/json;charset=utf-8");PrintWriter out = resp.getWriter();resp.setStatus(200);Map<String, Object> map = new HashMap<>();map.put("status", 200);map.put("msg", principal);ObjectMapper objectMapper = new ObjectMapper();out.write(objectMapper.writeValueAsString(map));out.flush();out.close();}};}以下是登錄失敗的回調函數,根據 Spring Security 拋出的異常信息,將錯誤信息以 JSON 形式返回:
public AuthenticationFailureHandler failureHandler() {return new AuthenticationFailureHandler() {@Overridepublic void onAuthenticationFailure(HttpServletRequest req,HttpServletResponse resp,AuthenticationException e)throws IOException, ServletException {resp.setContentType("application/json;charset=utf-8");PrintWriter out = resp.getWriter();resp.setStatus(401);Map<String, Object> map = new HashMap<>();map.put("status", 401);if (e instanceof LockedException) {map.put("msg", "賬戶被鎖定!");} else if (e instanceof BadCredentialsException) {map.put("msg", "用戶名或密碼錯誤!");} else if (e instanceof DisabledException) {map.put("msg", "賬戶被禁用!");} else if (e instanceof AccountExpiredException) {map.put("msg", "賬戶已過期!");} else if (e instanceof CredentialsExpiredException) {map.put("msg", "密碼已過期!");} else {map.put("msg", "登錄失敗!");}ObjectMapper objectMapper = new ObjectMapper();out.write(objectMapper.writeValueAsString(map));out.flush();out.close();}};}9、攔截登錄認證測試
數據庫的初始數據如下:
注意:role_name 前要加上前綴 ROLE_
運行項目,瀏覽器地址欄中輸入 localhost:8080,可以成功訪問到首頁,因為我們在前面放行了 / 請求
點擊下方的 3 個超鏈接,將被 Spring Security 攔截,自動跳轉到我們自定義的登錄界面
在接口測試工具中,測試登錄功能
密碼錯誤,執行失敗回調,返回的 JSON 中可以看到對應的錯誤信息
用戶名和密碼都正確,執行成功回調,返回該角色的所有信息
接著在,單選框中勾選 記住我 ,然后提交登錄,可以看到多了一個名為 remember-me 的 cookie
由于 root 具有 sadmin 以及 admin 權限,所以訪問以 /sadmin 或 /admin 開頭的請求都能夠成功
root 不具備 user 權限,故訪問以 /user 開頭的請求將別攔截,403 錯誤表示認證不通過
其他兩個用戶的結果也可以類似的測試出來,這里就不再細說了。
以上就是 Spring Security 中一些常用功能的使用了,我們對一些常用的安全管理框架還是要有所了解與掌握的。
總結
以上是生活随笔為你收集整理的Spring Security | 轻松搞定认证授权~的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一篇搞定 SpringBoot+Myba
- 下一篇: SpringBoot 就这一篇全搞定