Spring Security極簡入門三部曲(下篇)

Spring Security極簡入門三部曲(下篇)

前情回顧

我們已經實現的功能:

  1. 網站分為首頁、登陸頁、用戶頁、管理員頁、報錯頁
  2. 使用用戶名密碼登陸,登陸失敗報錯
  3. 首頁、登陸頁所有角色都能訪問
  4. 用戶頁需要USER或ADMIN許可權,管理員頁需要ADMIN許可權(許可權不足時跳轉至403頁面)
  5. 如果用戶沒有登錄,則訪問需要許可權的頁面時自動跳轉登錄頁面,登陸成功後自動跳轉至訪問的頁面
  6. 自定義驗證器,當黑客使用baize用戶和任意密碼登陸後,將獲取全部許可權

本次添加的功能:

從這篇部落格開始,我們將在demo2中加入資料庫得到demo3,在保留demo2的功能的基礎上,增加兩個用戶,user2,擁有USER許可權,admin2,擁有USER、ADMIN許可權,這些數據都將存入資料庫中,github項目地址

資料庫設計

回顧一下之前給出的資料庫模型

當然,本節並不會用到所有的表,規定只在用戶<->角色之間進行許可權的約束,也就是上圖的用戶表、角色表、用戶角色關係表,建表語句如下:

create table `user`(
	`user_id` int(11) not null auto_increment,
	`username` varchar(255) default null comment '姓名',
	`password` varchar(255) default null comment '密碼',
	primary key (`id`)
)engine=innodb, charset=utf8;

create table `role`(
	`role_id` int(11) not null auto_increment,
	`role_name` varchar(255) default null comment '角色',
	primary key (`id`)
)engine=innodb, charset=utf8;

create table `user_role`(
	`user_id` int(11) not null,
	`role_id` int(11) not null,
	primary key(`user_id`, `role_id`)
)engine=innodb, charset=utf8;

//這個密碼是用了工具類對123進行加密後的結果
insert into `user`(`user_id`, `username`, `password`)
values
(1, 'admin2', '$2a$10$h6rzOMVI5lismIclafb7duoVCgN2ShCVr4Nn2Jx772.buyaq7rZKq'),
(2, 'user2', '$2a$10$h6rzOMVI5lismIclafb7duoVCgN2ShCVr4Nn2Jx772.buyaq7rZKq');

insert into `role`(`role_id`, `role_name`)
values
(1, 'ROLE_USER'),
(2, 'ROLE_ADMIN');

insert into `user_role`(`user_id`, `role_id`)
values
(1, 1),
(1, 2),
(2, 1);

demo時刻

實現功能:github項目地址

  1. 從資料庫中讀取用戶名、密碼,與前端輸l入的資訊進行對比驗證(user2和admin2,密碼為123)
  2. 驗證通過後,登陸用戶會得到資料庫中存儲的角色資訊

demo3相比前兩個demo增加了較多的文件:

  1. application.yml中添加了資料庫相關的一些配置文件
  2. 因使用mybatis框架而添加了相關mapper,以及配合使用的實體類,service,xml文件等
  3. 添加了一個UserDetailsService介面的實現類MyUserDetailsService為核心類

程式碼分析

MyUserDetailsService是核心,它的主要作用就是自定義了一個資料庫驗證器加入過濾器鏈,用於驗證前端輸入的用戶名是否能在資料庫中表中查到,並且在查得用戶時去查詢角色表,為用戶賦予角色許可權(spring security會為我們做密碼錯誤時的驗證,不用人為去處理)

我們只需要在自定義的loadUserByUsername()方法中將參數s當作前端輸入的username去使用,並且最後返回一個UserDetails介面的實現類即可(該實現類記錄著用戶名、密碼、許可權列表)

/**
 * UserDetailsService的實現類,用於在程式中引入一個自定義的AuthenticationProvider,實現資料庫訪問模式的驗證
 */
@Service
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private UserRoleService userRoleService;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        //從資料庫中取出用戶資訊
        User user = userService.findByName(s);
        //判斷用戶是否存在
        if (user == null) {
            throw new UsernameNotFoundException("資料庫中無此用戶!");
        }
        //添加許可權
        List<UserRole> userRoles = userRoleService.listByUserId(user.getUserId());
        for (UserRole userRole : userRoles) {
            Role role = roleService.findById(userRole.getRoleId());
            authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
    }
}

小結

  1. 在demo2的基礎上,我們增加了兩個用戶,我們可以使用這兩個用戶進行登陸,也可以使用之前定義在記憶體中的賬戶進行登陸

  2. 本節的demo3看上去多了很多內容但大多都是mybatis框架的程式碼和資料庫的配置文件,請主要關注MyUserDetailsService實現類,並將其當作定義了一個資料庫驗證器類,定義了過濾器鏈中的一個驗證規則(類似於一個自定義的AuthenticationProvider實現類)