1、前言
為了友善進行項目開發,自己搭建了一套簡單的基于RBAC模型的權限管理系統,其中涉及到的使用者、角色、資源(菜單)等子產品已經實作了,現在需要引入SpringSecurity來實作認證和授權。
2、引入依賴
這裡使用的SpringSecurity的版本是:5.0.8.RELEASE,SpringBoot版本是:2.0.5.RELEASE。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3、建立UserDetailsService實作類
UserDetailsService實作類,用于加載使用者資訊。主要實作了UserDetailsService 接口的loadUserByUsername()方法,根據使用者名從資料庫查詢使用者資訊和使用者權限資訊,最後封裝成了UserDetails 對象。
@Component("userDetailsService")
public class QriverUserDetailsService implements UserDetailsService {
private Logger logger = LoggerFactory.getLogger(QriverUserDetailsService.class);
@Autowired
private UserService userService;
@Autowired
private UserRoleService userRoleService;
@Autowired
private RoleService roleService;
/**
* 根據username加載資料庫中的使用者,并建構UserDetails對象。
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> authorities = new ArrayList<>();
User param = new User(username);
User user = userService.querySingle(param);
if(user == null) {
logger.info("使用者名不存在,使用者名:" + username);
throw new UsernameNotFoundException("使用者名不存在");
}
UserRole userRole = new UserRole();
userRole.setUserId((String) user.getId());
List<UserRole> userRoles = userRoleService.queryList(userRole);
for(UserRole uRole : userRoles) {//儲存Role的ID
authorities.add(new SimpleGrantedAuthority(uRole.getRoleName()));
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),authorities);
}
}
4、建立PasswordEncoder實作類
PasswordEncoder實作類,從5.0版本開始強制要求設定,主要用來配置加密方式,這裡使用了明文的方式。
@Component("nonPasswordEncoder")
public class QriverNonPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
//加密方法可以根據自己的需要修改
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encode(rawPassword).equals(encodedPassword);
}
}
5、配置SpringSecurity
首先,定義SpringSecurityConfig 配置類,該類繼承了WebSecurityConfigurerAdapter 抽象類。
然後,添加注解@Configuration、@EnableWebSecurity,分别表示配置類和開啟 Security 服務。
然後,重寫configure(AuthenticationManagerBuilder auth)方法,該方法主要用來配置AuthenticationManager對象相關内容,這裡主要配置了自定義的UserDetailsService實作類和PasswordEncoder實作類。
最後,通過重寫configure(HttpSecurity http)方法,進行配置HttpSecurity 相關内容,主要配置了那些URL需要鑒權那些不需要鑒權,自定義登入頁面、登入成功或失敗時跳轉路徑、異常攔截等内容。
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private QriverUserDetailsService userDetailsService;
@Autowired
private QriverNonPasswordEncoder nonPasswordEncoder;
/**
* 配置AuthenticationManager對象
* @param auth
* @throws Exception
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//校驗使用者
auth.userDetailsService( userDetailsService )
.passwordEncoder(nonPasswordEncoder);
}
/**
* 配置 HttpSecurity
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//設定不需要鑒權的位址
.antMatchers("/error","/","index","/login","/login-error","/401","/static/**").permitAll()
//其他請求均需要鑒權
.anyRequest().authenticated()
.and()
//登入成功跳轉頁面
.formLogin().defaultSuccessUrl("/index/toIndex").
//自定義登入界面
loginPage( "/login" ).
//登入失敗跳轉頁面
failureUrl( "/login-error" )
.and()
.exceptionHandling().accessDeniedPage( "/401" )
.and().csrf().disable()
.headers().frameOptions().disable();
http.logout().logoutSuccessUrl( "/" );
}
}
關于"/login"、"/login-error"、"/index/toIndex"等api接口,這裡不再貼出代碼了。
6、登入頁面
這裡就是一個普通的from表單,最重要的就是from表單的action方法,這裡使用了“/login”,且method=“post”,“/login”是SpringSecurity預設的位址,可以在HttpSecurity 中進行配置。
這裡需要注意的是,之前自己實作的登入驗證,需要首先定義一個“/doLogin”方法,然後登入界面通過from表單或AJAX進行請求,而在SpringSecurity中,則不需要再定義該方法,預設已經實作了,隻需要通過HttpSecurity的loginProcessingUrl()方法修改預設的請求的路徑位址即可。
//部分代碼
<body class="gray-bg">
<div class="middle-box text-center loginscreen animated fadeInDown">
<div>
<div style="margin:25% 0 5% 0;">
<img src="${ctxPath}/static/img/logo.png" width="45%;">
</div>
<h3>歡迎使用 綜合管理平台</h3>
<form class="m-t" role="form" method="post" action="${ctxPath}/login">
<div class="form-group">
<input type="text" id="username" name="username" class="form-control" placeholder="使用者名" value="admin" required="">
</div>
<div class="form-group">
<input type="password" id="password" name="password" class="form-control" placeholder="密碼" value="123456" required="">
</div>
<button type="submit" class="btn btn-primary block full-width m-b">登 錄</button>
</form>
</div>
</div>
</body>
7、其他
通過上述步驟,我們基本上已經實作了SpringSecurity的內建,這個時候我們的使用者鑒權就會通過SpringSecurity被保護起來了。後續,我們會繼續細化一些細節,比如使用者名密碼錯誤時,如何更加友好提示?如何登入後,還回到目前頁面等功能。