Spring?Security使用數(shù)據(jù)庫登錄認證授權
一、搭建項目環(huán)境
1、創(chuàng)建 RBAC五張表
RBAC,即基于角色的權限訪問控制(Role-Based Access Control),就是用戶通過角色與權限進行關聯(lián)。
在這種模型中,用戶與角色之間,角色與權限之間,一般者是多對多的關系。
在 MySQL數(shù)據(jù)庫中,創(chuàng)建如下幾個表:
DROP TABLE IF EXISTS sys_user; CREATE TABLE sys_user( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵id' , username VARCHAR(60) COMMENT '用戶名' , password VARCHAR(255) COMMENT '密碼' , status tinyint(1) COMMENT '用戶狀態(tài),1-開啟-0禁用' , password_non_expired tinyint(1) COMMENT '密碼是否失效,1-可用,0-失效' , PRIMARY KEY (id) ) COMMENT = '用戶表'; DROP TABLE IF EXISTS sys_role; CREATE TABLE sys_role( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵id' , role_name VARCHAR(64) NOT NULL COMMENT '角色名' , role_desc VARCHAR(64) NOT NULL COMMENT '角色描述' , PRIMARY KEY (id) ) COMMENT = '角色表'; DROP TABLE IF EXISTS sys_permission; CREATE TABLE sys_permission( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵id' , parent_id BIGINT COMMENT '父id' , permission_name VARCHAR(64) COMMENT '菜單名稱' , permission_url VARCHAR(255) COMMENT '菜單地址' , PRIMARY KEY (id) ) COMMENT = '權限表'; DROP TABLE IF EXISTS sys_user_role; CREATE TABLE sys_user_role( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵id' , user_id BIGINT NOT NULL COMMENT '用戶id' , role_id BIGINT COMMENT '角色id' , enabled tinyint(1) DEFAULT 1 COMMENT '是否有效' , PRIMARY KEY (id) ) COMMENT = '用戶角色關聯(lián)表'; DROP TABLE IF EXISTS sys_role_permission; CREATE TABLE sys_role_permission( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵id' , role_id BIGINT NOT NULL COMMENT '角色id' , permission_id BIGINT COMMENT '權限id' , PRIMARY KEY (id) ) COMMENT = '角色權限表';
2、創(chuàng)建項目
創(chuàng)建 Mavne 項目,Springboot + Spring Security + MyBatis + MySQL + jsp。
1)配置文件如下
server: port: 9090 # jsp配置 spring: mvc: view: prefix: /pages/ suffix: .jsp datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/security_authority?useUnicode=true;characterEncoding=utf8;useSSL=true;serverTimezone=GMT username: root password: 123456 # mybatis配置 mybatis: configuration: map-underscore-to-camel-case: true mapper-locations: classpath:mybatis/mapper/*.xml logging: level: com.charge.learn.springsecurity.springboot.security.jsp.dao: debug
2)啟動類
@SpringBootApplication @MapperScan("com.charge.learn.springsecurity.springboot.security.jsp.dao") public class SpringSecurityApplication { public static void main(String[] args) { SpringApplication.run(SpringSecurityApplication.class, args); } }
二、整合 Spring Security實現(xiàn)用戶認證
1、后端整合
1.1 用戶
Spring Security的用戶對象是 UserDetail類型
,Spring Security在認證流程中只認 UserDetail用戶。
- 通過
UserDetailsService
的 loadUserByUsername方法獲取 UserDetail用戶。 - 認證成功之后,調用的是這個帶有三個參數(shù)的 UsernamePasswordAuthenticationToken構造方法,將 角色信息添加到了
ArrayList<GrantedAuthority>
集合中。 - 在 successfulAuthentication 方法中,將認證信息存儲到了SecurityContext中。
UserDetail接口的方法(根據(jù)用戶業(yè)務來處理這幾個值)。
- boolean enabled 賬戶是否可用
- boolean accountNonExpired 賬戶是否失效
- boolean credentialsNonExpired 賬戶密碼是否失效
- boolean accountNonLocked 賬戶是否鎖定
- Collection<? extends GrantedAuthority> getAuthorities() 獲取賬戶的所有權限(用戶角色)
注意:
四個布爾類型的參數(shù)都為 true時,然后成功,否則,有一個為 false,就會認證失敗。
所以,我們可以將我們的用戶封裝成 UserDetail對象。
這里我們讓用戶對象實現(xiàn) UserDetail接口,那么我們用戶就屬于 UserDetail類型的用戶,然后實現(xiàn)接口的方法。
public class SysUser implements UserDetails { private Long id; private String username; private String password; private Boolean status; //用戶狀態(tài),1-開啟-0禁用 private Boolean passwordNonExpired; //密碼是否失效,1-可用,0-失效 /** * 用戶關聯(lián)的所有角色 */ private List<SysRole> roles = new ArrayList<>(); //get、set方法 //標記該字段不做json處理 @JsonIgnore @Override public Collection<? extends GrantedAuthority> getAuthorities() { return roles; } @JsonIgnore @Override public boolean isAccountNonExpired() { return true; } @JsonIgnore @Override public boolean isAccountNonLocked() { return true; } @JsonIgnore @Override public boolean isCredentialsNonExpired() { return passwordNonExpired == null ? false : passwordNonExpired; } @JsonIgnore @Override public boolean isEnabled() { return status == null ? false : status; } }
1.2 角色
Spring Security的權限對象是 GrantedAuthority
類型。通過它的值來實現(xiàn)權限管理的。
所以,我們讓角色對象實現(xiàn)GrantedAuthority接口,那么我們角色就屬于 GrantedAuthority類型,然后實現(xiàn)接口的方法。
上面用戶可以直接將 角色添加到 Collection<? extends GrantedAuthority>集合中。
public class SysRole implements GrantedAuthority { private Long id; private String roleName; private String roleDesc; //get、set方法 //標記該字段不做json處理 @JsonIgnore @Override public String getAuthority() { return roleName; } }
1.3 SysUserService接受繼承UserDetailsService類
Spring Security在認證流程中通過 UserDetailsService
的 loadUserByUsername方法獲取 UserDetail用戶。
所以,我們讓 UserService接口繼承 UserDetailsService類
,然后重寫 loadUserByUsername方法。
在 loadUserByUsername方法中,獲取我們的用戶信息( UserDetail類型),記得將用戶關聯(lián)的角色也賦值為用戶信息。
public interface SysUserService extends UserDetailsService { void save(SysUser user); }
@Service @Transactional public class SysUserServiceImpl implements SysUserService { @Autowired private SysUserMapper sysUserMapper; @Autowired private SysRoleMapper sysRoleMapper; @Autowired private BCryptPasswordEncoder passwordEncoder; @Override public void save(SysUser sysUser) { // 將密碼加密入庫 sysUser.setPassword(passwordEncoder.encode(sysUser.getPassword())); sysUserMapper.insert(sysUser); } /** * 認證業(yè)務 * * @param username * - 用戶在瀏覽器輸入的用戶名 * @return UserDetails - Spring Security的用戶對象,返回 null表示認證失??! * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { /** * 用戶信息和角色信息可以一步關聯(lián)查詢到位得到SysUser,我這里分開查詢 */ // 1.查詢用戶 SysUser sysUser = sysUserMapper.getByUsername(username); if (sysUser == null) { return null; } // 2.獲取用戶關聯(lián)的所有角色 List<SysRole> sysRoles = sysRoleMapper.listAllByUserId(sysUser.getId()); sysUser.setRoles(sysRoles); System.out.println("====> sysUser=" + sysUser.toString()); return sysUser; } }
mapper.xml中的幾個方法
<select id="getByUsername" resultMap="BaseResultMap"> select id, username, password, status, password_non_expired from sys_user where username = #{username} </select> <select id="listAllByUserId" resultMap="BaseResultMap"> SELECT r.id, r.role_name role_name, r.role_desc role_desc FROM sys_role r, sys_user_role ur WHERE r.id = ur.role_id AND ur.user_id = #{userId} </select>
1.4 創(chuàng)建 SpringSecurity配置類
自定義一個配置類,添加@EnableWebSecurity注解
,并繼承WebSecurityConfigurerAdapter類
。然后就擁有了 SpringSecutiry的所有默認配置。我們也可以修改配置。
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SysUserService userService; // 加密對象注入IOC容器 @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } // 1.指定認證對象的來源(內存或者數(shù)據(jù)庫),指定加密方式 @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } //2. SpringSecurity配置相關信息 @Override public void configure(HttpSecurity http) throws Exception { // 釋放靜態(tài)資源,指定攔截規(guī)則,指定自定義的認證和退出頁面,csrf配置等 http.authorizeRequests() // 指定攔截規(guī)則 .antMatchers("/login.jsp", "failer.jsp", "/css/**", "/img/**", "/plugins/**").permitAll() //釋放這些資源,不攔截 .antMatchers("/**").hasAnyRole("USER", "ADMIN") //所有資源都需要這些角色中的一個 .anyRequest().authenticated() //其他請求,必須認證通過之后才能訪問 .and() // 表示新的一個配置開始 // 指定自定義的認證頁面 .formLogin() .loginPage("/login.jsp") .loginProcessingUrl("/login") .successForwardUrl("/index.jsp") .failureForwardUrl("/failer.jsp") .permitAll() // 釋放這些資源,不攔截登錄 .and() // 指定自定義的退出頁面 .logout() .logoutSuccessUrl("/logout") .invalidateHttpSession(true) // 清楚session .logoutSuccessUrl("/login.jsp") .permitAll() //.and() // 禁用csrf配置,默認開啟的(一般不寫,頁面要加csrf),這里我們測試下 // .and() // .csrf() // .disable() ; }
主要配置信息如下:
- 指定認證對象 SysUserService (UserDetailsService類型)
- 指定了用戶密碼使用的加密對象
- SpringSecurity配置相關信息,比如:指定攔截規(guī)則,指定自定義的認證頁面,csrf等。
2、前端整合
在 Spring Security 中,如果我們不做任何配置,默認的登錄頁面和登錄接口的地址都是 /login,即默認會存在如下兩個請求:
- GET http://localhost:8080/login
- POST http://localhost:8080/login
如果是 GET 請求表示你想訪問登錄頁面,如果是 POST 請求,表示你想提交登錄數(shù)據(jù)。默認的表單字段為 username和password。
SpringSecurity 默認 是開啟 csrf防護機制。
所以,在自定義的表單上添加上 _csrf隱藏input(必須要寫在form表單里面)。
引入 security標簽庫 <%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%> <security:csrfInput/>
啟動項目,登錄認證訪問ok.
三、整合 Spring Security實現(xiàn)用戶授權
認證過程獲取用戶信息時,我們已經(jīng)把用戶關聯(lián)的角色信息設置到了 UserDetails中,所以,我們只需要分配 資源訪問的角色就可以了。
1、后端
1.1 開啟 Spring Security權限控制
Spring Security可以通過注解的方式來控制類或者方法的訪問權限。支持開啟權限控制的注解類型如下:
- jsr250-annotations:表示支持 jsr250-api的注解
- pre-post-annotations:表示支持 spring表達式注解
- secured-annotations:這才是 Spring Security提供的注解
在實際開發(fā)中,用一類即可,三個都開啟也沒關系。
在 SpringSecurity配置類上 添加 @EnableGlobalMethodSecurity注解
,表示開啟 Spring Security權限控制,這里我們三類都開啟了。
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled = true, jsr250Enabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ...
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled = true, jsr250Enabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ...
1.2 在角色對應類或者方法上添加權限注解
1.2.1 使用 Spring Security注解
- @Secured({“ROLE_ADMIN”,“ROLE_PRODUCT”})
@Controller @RequestMapping("/user") @Secured("ROLE_ADMIN") //表示當前類中所有方法需要 ROLE_ADMIN才能訪問 public class UserController { @Autowired private UserService userService; @RequestMapping("/findAll") public String findAll(Model model){ List<SysUser> list = userService.findAll(); model.addAttribute("list", list); return "user-list"; } 。。。 }
1.2.2 使用 Spring表達式注解
- @PreAuthorize(“hasAnyRole(‘ROLE_ADMIN’,‘ROLE_PRODUCT’)”)
@Controller @RequestMapping("/product") public class ProductController { @RequestMapping("/findAll") //表示當前類中findAll方法需要 ROLE_ADMIN或者 ROLE_PRODUCT才能訪問 @PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_PRODUCT')") public String findAll(){ return "product-list"; } }
1.2.3 使用 JSR-250注解
- @RolesAllowed({“ROLE_ADMIN”,“ROLE_User”})
@Controller @RequestMapping("/order") @RolesAllowed({"ROLE_ADMIN","ROLE_USER"}) //表示當前類中所有方法都需要ROLE_ADMIN或者ROLE_User才能訪問 public class OrderController { @RequestMapping("/findAll") public String findAll(){ return "order-list"; } }
2、前端
在jsp業(yè)頁面中,對每個菜單資源通過 SpringSecurity標簽庫指定訪問所需的角色。
<%@taglib uri="http://www.springframework.org/security/tags" prefix="security" %> <!-- 指定訪問所需角色 --> <security:authorize access="hasAnyRole('ROLE_ADMIN', '等等')">
啟動項目,通過不同的用戶登錄,授權訪問ok.
到此這篇關于Spring Security使用數(shù)據(jù)庫登錄認證授權的文章就介紹到這了,更多相關Spring Security數(shù)據(jù)庫登錄認證授權內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- SpringBoot+SpringSecurity+JWT實現(xiàn)系統(tǒng)認證與授權示例
- mall整合SpringSecurity及JWT認證授權實戰(zhàn)下
- mall整合SpringSecurity及JWT實現(xiàn)認證授權實戰(zhàn)
- Spring Security+JWT實現(xiàn)認證與授權的實現(xiàn)
- Java Spring Security認證與授權及注銷和權限控制篇綜合解析
- SpringSecurity數(shù)據(jù)庫進行認證和授權的使用
- SpringBoot+SpringSecurity實現(xiàn)基于真實數(shù)據(jù)的授權認證
- Spring Security OAuth2認證授權示例詳解
- Spring Security實現(xiàn)身份認證和授權的示例代碼
相關文章
Spring?MVC基于注解的使用之JSON數(shù)據(jù)處理的方法
這篇文章主要介紹了Spring?MVC基于注解的使用JSON數(shù)據(jù)處理,json是一種輕量級的數(shù)據(jù)交換格式,是一種理想的數(shù)據(jù)交互語言,它易于閱讀和編寫,同時也易于機器解析和生成,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2022-05-05在IDEA中創(chuàng)建SpringBoot項目的詳細步驟
這篇文章主要給大家介紹了在IDEA中創(chuàng)建SpringBoot項目的詳細步驟,文中有詳細的圖文介紹和代碼示例,對大家的學習和工作有一定的幫助,需要的朋友可以參考下2023-09-09JSON中fastjson、jackson、gson如何選擇
在Java中,JSON的解析方式很多,例如fastjson(阿里)、Gson(谷歌)、jackjson等,本文主要介紹了JSON中fastjson、jackson、gson如何選擇,具有一定的參考價值,感興趣的可以了解一下2021-12-12spring源碼閱讀--@Transactional實現(xiàn)原理講解
這篇文章主要介紹了spring源碼閱讀--@Transactional實現(xiàn)原理講解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09SpringBoot生產(chǎn)環(huán)境打包如何去除無用依賴
這篇文章主要介紹了SpringBoot生產(chǎn)環(huán)境打包如何去除無用依賴問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09