認證+授權程式碼實現
Spring Security是 一種基於 Spring AOP 和 Servlet 過濾器的安全框架。它提供全面的安全性解決方案,同時在 Web 請求級和方法呼叫級處理身份確認和授權。
有關認證和授權的理論知識,之前有寫過相關部落格。瞭解許可權管理
一、SpringSceurity工作流程
網上找一張圖,覺得畫的挺好的,比較容易理解。不然換的是原始碼流程圖很難去理解。
圖片地址 : 地址 可以單機放大看更加清楚
要想理解這張圖建議看下這篇部落格,因為這張圖中需要自定義的My...類,在文章中都有說明,所以更好理解點。
二、認證+授權程式碼
這裡只展示一些核心程式碼,具體完整程式碼放在github上。
1、UserDetails介面
Security 中的使用者介面,我們自定義使用者類要實現該介面, 用於向security中注入當前使用者的姓名密碼,和擁有的角色。同時也包含一些其它資訊,比如當前使用者是否過期,
賬號是否鎖定等等。
自己定義User實現這個介面
public class User implements UserDetails {
private String username;
private String password;
private List<Role> roles;
/**
* 獲取使用者名稱
*/
@Override
public String getUsername() {
return username;
}
/**
* 獲取密碼
*/
@Override
public String getPassword() {
return password;
}
/**
* 使用者的許可權集, 預設需要新增ROLE_ 字首
*/
@Override
@JsonIgnore
public List<GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
}
return authorities;
}
/**
* 賬戶是否過期
*/
@Override
@JsonIgnore
public boolean isAccountNonExpired() {
return true;
}
/**
* 賬戶是否鎖定
*/
@Override
@JsonIgnore
public boolean isAccountNonLocked() {
return true;
}
/**
* 憑證是否過期
*/
@Override
@JsonIgnore
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 使用者是否可用
*/
@Override
public boolean isEnabled() {
return true;
}
}
2、UserDetailsService
Security 中的使用者 Service,自定義使用者服務類需要實現該介面。這個介面只有一個方法需要我們去實現,那就是通過使用者名稱去獲取使用者資訊。這裡也是和資料庫互動獲取
使用者認證和授權資訊的地方。
@Service
@Slf4j
public class UserService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
//TODO 正常應該查詢資料庫獲取使用者和使用者的許可權
// User user = userMapper.loadUserByUsername(userName);
// List<Role> roles = rolesMapper.getRolesByUid(user.getId());
// user.setRoles(roles);
log.info("登陸使用者名稱: {}", userName);
//通過使用者名稱查詢到的密碼 密碼肯定是加密過的 這裡明文密碼是 123456
String password = "e10adc3949ba59abbe56e057f20f883e";
//使用者對應許可權
List<Role> roles = Lists.newArrayList(new Role(1L, "教師"), new Role(2L, "學生"));
User user = new User(userName, password, roles);
return user;
}
}
注意
這裡的明文密碼是 123456,也就是使用者輸入這個才能完成認證。授權的話當前使用者有兩個角色 教師
和 學生
。在下面測試的時候會用到。
3、WebSecurityConfigurerAdapter
它是Spring Security的Java 配置類。建立類SecurityConfiguration繼承 WebSecurityConfigurerAdapter
,來對我們應用中所有的安全相關的事項(
所有url,驗證使用者名稱密碼,表單重定向等)進行控制。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 1、配置的是認證資訊, AuthenticationManagerBuilder 這個類,就是AuthenticationManager的建造者, 我們只需要向這個類中, 配置使用者資訊,
* 就能生成對應的AuthenticationManager, 這個類也提過,是使用者身份的管理者, 是認證的入口, 因此,我們需要通過這個配置,想security提供真實的使用者身份。
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
/**
* 2、配置Security的認證策略, 每個模組配置使用and結尾。這個也是最複雜的
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
}
/**
* 3、這個配置方法用於配置靜態資源的處理方式,可使用 Ant 匹配規則。就是可以不用認證就可以直接訪問的介面
*/
@Override
public void configure(WebSecurity web) throws Exception {
}
}
完整示例
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
/**
* 密碼驗證器
*/
@Autowired
private PassWordEncorder passWordEncorder;
/**
* 成功處理器
*/
@Autowired
private AuthenctiationSuccessHandler authenctiationSuccessHandler;
/**
* 失敗處理器
*/
@Autowired
private AuthenctiationFailHandler authenctiationFailHandler;
/**
* 向Security注入使用者資訊
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passWordEncorder);
}
/**
* 配置規則
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//開啟登陸配置
http.authorizeRequests()
// 登入之後就能訪問
.antMatchers("/no-authorize").authenticated()
// 登陸後 需要校長角色許可權
.antMatchers("/need-authorize").hasRole("校長")
// 其他的路徑都是登入後即可訪問
.anyRequest().authenticated()
.and().formLogin()
// 定義登入頁面,未登入時,訪問一個需要登入之後才能訪問的介面,會自動跳轉到該頁面
.loginPage("/login_page")
//登入成功的處理器
.successHandler(authenctiationSuccessHandler)
//登入失敗的處理器
.failureHandler(authenctiationFailHandler)
// 登入處理介面
.loginProcessingUrl("/login")
// 定義登入時,使用者名稱的 key,預設為 username
.usernameParameter("username")
//定義登入時,使用者密碼的 key,預設為 password
.passwordParameter("password").permitAll()
.and().logout()
////和表單登入相關的介面統統都直接通過
.permitAll()
.and().csrf().disable().exceptionHandling().accessDeniedHandler(getAccessDeniedHandler());
}
/**
* 對於/static/ 下的路徑都不用認證
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/no-login");
}
/**
* 使用者未認證異常攔截
*/
@Bean
AccessDeniedHandler getAccessDeniedHandler() {
return new AuthenticationAccessDeniedHandler();
}
}
注意
這裡一共配置了三個路徑用於測試。
1、/no-login 介面不需要認證就可以直接訪問
2、/no-authorize 需要認證 但不需要授權就可以訪問
3、/need-authorize 首先需要認證 認證通過還需要授權 這裡需要校長的角色才可以訪問該介面 但是我們測試使用者只有教師和學生所以沒有許可權訪問該介面
下面會針對這個個介面分別進行測試。
三、測試
1、介面提供
@RestController
public class TestController {
/**
* 1、不需要登陸就可以訪問
*/
@RequestMapping(value = "/no-login")
public ServiceResponse noLogin() {
return ServiceResponse.success("歡迎訪問不需要登陸介面");
}
/**
* 2、只登陸,不許認證介面
*/
@RequestMapping(value = "/no-authorize")
public ServiceResponse needAuthorize(){
return ServiceResponse.success("登陸了 不用授權");
}
/**
* 3、登陸 + 相關認證介面
*/
@RequestMapping(value = "/need-authorize")
public ServiceResponse noAuthorize() {
return ServiceResponse.success("登陸+授權成功");
}
/**
* @Description: 如果自動跳轉到這個頁面,說明使用者未登入,返回相應的提示即可
*/
@RequestMapping("/login_page")
public ServiceResponse loginPage() {
return ServiceResponse.failure("001", "尚未登入,請登入!");
}
}
2、未登入訪問 no-login 和 no-authorize 介面
no-login
介面
很明顯沒有登陸 請求該介面成功!
no-authorize
介面
沒有登陸訪問失敗,在上面配置瞭如果使用者沒有認證的話跳轉到login_page介面,所以這裡返回 '尚未登入,請登入!'
3、登陸後訪問 no-authorize 和 need-authorize 介面
先登陸
根據上面配置登陸的路徑為 /login 請求引數包括 username 和 password
注意
這裡需要post請求。
no-authorize
介面
登陸就可以訪問了。
need-authorize
介面
雖然登陸成功了,但是因為該介面需要校長角色,之前給該使用者只配置了教師和學生的角色所以訪問失敗。
參考
別人罵我胖,我會生氣,因為我心裡承認了我胖。別人說我矮,我就會覺得好笑,因為我心裡知道我不可能矮。這就是我們為什麼會對別人的攻擊生氣。
攻我盾者,乃我內心之矛(17)