日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Security | 轻松搞定认证授权~

發布時間:2024/3/13 javascript 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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,提高效率
<dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-java8time</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.1</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>

2、創建數據庫,搭建環境

一共有如下 3 張表:

  • user:用戶表
  • role:角色(權限)表
  • user_role:多對多關聯表

    創建數據庫 springboot-data,建表的 SQL 如下:
DROP TABLE IF EXISTS `role`;CREATE TABLE `role` (`role_id` int(10) NOT NULL AUTO_INCREMENT,`role_name` varchar(30) NOT NULL,`role_detail` varchar(30) NOT NULL,PRIMARY KEY (`role_id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='角色表';insert into `role`(`role_id`,`role_name`,`role_detail`) values (1,'ROLE_sadmin','超級管理員'),(2,'ROLE_admin','管理員'),(3,'ROLE_user','普通用戶');DROP TABLE IF EXISTS `user`;CREATE TABLE `user` (`id` int(10) NOT NULL AUTO_INCREMENT,`name` varchar(20) NOT NULL,`pwd` varchar(30) NOT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='用戶表';insert into `user`(`id`,`name`,`pwd`) values (1,'root','123456'),(2,'admin','123456'),(3,'study','123456');DROP TABLE IF EXISTS `user_role`;CREATE TABLE `user_role` (`id` int(10) NOT NULL AUTO_INCREMENT,`user_id` int(10) NOT NULL,`role_id` int(10) NOT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='用戶-角色表';insert into `user_role`(`id`,`user_id`,`role_id`) values (1,1,1),(2,1,2),(3,2,2),(4,3,3);

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/*.xml

4、創建實體類

創建 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 會自動拋出相關異常。

@Data @AllArgsConstructor @NoArgsConstructor public class User implements UserDetails {private Integer id;private String name;private String pwd;private Integer enabled;private Integer locked;private List<Role> roles;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<SimpleGrantedAuthority> authorities = new ArrayList<>();for (Role role : roles) {authorities.add(new SimpleGrantedAuthority(role.getRole_name()));}return authorities;}@Overridepublic String getPassword() {return pwd;}@Overridepublic String getUsername() {return name;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return locked == 0;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return enabled == 1;}}
  • isAccountNonExpired:賬戶是否未到期
  • isAccountNonLocked:賬戶是否未被鎖定
  • isCredentialsNonExpired:賬戶密碼是否未到期
  • isEnabled:賬戶是否可用

5、實現 UserMapper

UserMapper 接口中定義如下兩個方法:

  • queryUserByName :根據用戶名查詢用戶
  • queryRolesByUid:根據用戶 id 查詢角色
@Repository @Mapper public interface UserMapper {User queryUserByName(@Param("name") String name);List<Role> queryRolesByUid(@Param("id") int 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(),即不采取加密
@Autowiredprivate UserService userService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userService);}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/css/**", "/js/**", "/index.html","/img/**", "/fonts/**", "/favicon.ico");}@BeanPasswordEncoder passwordEncoder() {return 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
@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/").permitAll().antMatchers("/sadmin/**").hasRole("sadmin").antMatchers("/admin/**").hasRole("admin").antMatchers("/user/**").hasRole("user").anyRequest().authenticated().and().formLogin().loginPage("/toLogin").loginProcessingUrl("/login").permitAll().usernameParameter("name").passwordParameter("pwd").successHandler(successHandler()).failureHandler(failureHandler()).and().csrf().disable().logout().and().rememberMe().rememberMeParameter("remember");}

以下是登錄成功的回調函數,這里將用戶信息以 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 | 轻松搞定认证授权~的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。