Spring Security是 一種基於 Spring AOP 和 Servlet 過濾器的安全框架。它提供全面的安全性解決方案,同時在 Web 請求級和方法調用級處理身份確認和受權。html
有關認證和受權的理論知識,以前有寫過相關博客。瞭解權限管理java
網上找一張圖,以爲畫的挺好的,比較容易理解。否則換的是源碼流程圖很難去理解。git
圖片地址 : 地址 能夠單機放大看更加清楚github
要想理解這張圖建議看下這篇博客,由於這張圖中須要自定義的My...類,在文章中都有說明,因此更好理解點。web
Spring Boot Security 詳解spring
這裏只展現一些核心代碼,具體完整代碼放在github上。數據庫
Security 中的用戶接口,咱們自定義用戶類要實現該接口, 用於向security中注入當前用戶的姓名密碼,和擁有的角色。同時也包含一些其它信息,好比當前用戶是否過時,安全
帳號是否鎖定等等。app
本身定義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; } }
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,也就是用戶輸入這個才能完成認證。受權的話當前用戶有兩個角色 教師
和 學生
。在下面測試的時候會用到。
它是Spring Security的Java 配置類。建立類SecurityConfiguration繼承 WebSecurityConfigurerAdapter
,來對咱們應用中全部的安全相關的事項(
全部url,驗證用戶名密碼,表單重定向等)進行控制。
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * 一、配置的是認證信息, AuthenticationManagerBuilder 這個類,就是AuthenticationManager的建造者, 咱們只須要向這個類中, 配置用戶信息, * 就能生成對應的AuthenticationManager, 這個類也提過,是用戶身份的管理者, 是認證的入口, 所以,咱們須要經過這個配置,想security提供真實的用戶身份。 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { } /** * 二、配置Security的認證策略, 每一個模塊配置使用and結尾。這個也是最複雜的 */ @Override protected void configure(HttpSecurity http) throws Exception { } /** * 三、這個配置方法用於配置靜態資源的處理方式,可以使用 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(); } }
注意
這裏一共配置了三個路徑用於測試。
一、/no-login 接口不須要認證就能夠直接訪問 二、/no-authorize 須要認證 但不須要受權就能夠訪問 三、/need-authorize 首先須要認證 認證經過還須要受權 這裏須要校長的角色才能夠訪問該接口 可是咱們測試用戶只有教師和學生因此沒有權限訪問該接口
下面會針對這個個接口分別進行測試。
@RestController public class TestController { /** * 一、不須要登錄就能夠訪問 */ @RequestMapping(value = "/no-login") public ServiceResponse noLogin() { return ServiceResponse.success("歡迎訪問不須要登錄接口"); } /** * 二、只登錄,不準認證接口 */ @RequestMapping(value = "/no-authorize") public ServiceResponse needAuthorize(){ return ServiceResponse.success("登錄了 不用受權"); } /** * 三、登錄 + 相關認證接口 */ @RequestMapping(value = "/need-authorize") public ServiceResponse noAuthorize() { return ServiceResponse.success("登錄+受權成功"); } /** * @Description: 若是自動跳轉到這個頁面,說明用戶未登陸,返回相應的提示便可 */ @RequestMapping("/login_page") public ServiceResponse loginPage() { return ServiceResponse.failure("001", "還沒有登陸,請登陸!"); } }
no-login
接口
很明顯沒有登錄 請求該接口成功!
no-authorize
接口
沒有登錄訪問失敗,在上面配置了若是用戶沒有認證的話跳轉到login_page接口,因此這裏返回 '還沒有登陸,請登陸!'
先登錄
根據上面配置登錄的路徑爲 /login 請求參數包括 username 和 password
注意
這裏須要post請求。
no-authorize
接口
登錄就能夠訪問了。
need-authorize
接口
雖然登錄成功了,可是由於該接口須要校長角色,以前給該用戶只配置了教師和學生的角色因此訪問失敗。
別人罵我胖,我會生氣,由於我內心認可了我胖。別人說我矮,我就會以爲可笑,由於我內心知道我不可能矮。這就是咱們爲何會對別人的攻擊生氣。 攻我盾者,乃我心裏之矛(17)