一.前言
- 環境:springboot 2.3.0、springsecurity 5.3.2
- 這篇隨筆可能對其他人很不友好,因為只貼了相關的代碼,所以不建議大家參考,我只是寫給自己看的,但是跟大家分享一下;
- 如果代碼有問題的話請告訴我,但是其他的如代碼不規范什么的就不必了,我知道我很渣,存在很多問題;
- 管理員這個實體類只有 id、username 和 password 這三個屬性
- 該代碼沒有權限控制,沒有會話管理等其他的功能,只有登錄驗證功能,所以較為簡單
- 下面每個各種處理器都必須在第6點中的 MySecurityConfiguration 中注冊,否則無法生效
- 代碼已經驗證過,沒問題
二.代碼
導包(略……)
1.自定義登錄邏輯(訪問數據庫)
/** * @author Chase Meng * @description: 自定義登錄邏輯(訪問數據庫) * @created on 2020/9/7 */ @Configuration public class CustomizeUserDetailsService implements UserDetailsService { @Autowired private LoginOperate loginOperate; @Autowired private PasswordEncoder passwordEncoder; // @Autowired // private Encryption encryption; // @Bean // public PasswordEncoder passwordEncoder() { // return new BCryptPasswordEncoder(); // } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Admin admin =loginOperate.findUserByUsername(username); if(username==null||username.equals("")){ throw new RuntimeException("用戶名不能為空!"); } if(admin==null){ throw new RuntimeException("用戶名不存在!"); } List<GrantedAuthority> authorities=new ArrayList<GrantedAuthority>(); authorities.add(new SimpleGrantedAuthority("ROLE_"+"normal")); //由於沒有設置授權,所以隨便給一個身份normal User userDetails = new User(admin.getUsername(), passwordEncoder.encode(admin.getPassword()), authorities); return userDetails; } }
2.自定義登錄失敗處理器
** * @author Chase Meng * @description: 自定義登錄失敗處理器 * @created on 2020/9/7 */ @Component public class CustomizeAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { //super.onAuthenticationFailure(request, response, exception); Result result=new Result(); if(exception instanceof BadCredentialsException){ //密碼錯誤 result.setCode(1200); result.setMsg("密碼錯誤!"); }else if(exception instanceof InternalAuthenticationServiceException){ //用戶不存在 result.setCode(1100); result.setMsg("不存在該用戶!"); } else if(exception instanceof AccountExpiredException){ //賬號過期 } else if(exception instanceof CredentialsExpiredException){ //密碼過期 }else if(exception instanceof DisabledException){ //帳號不可用 }else if(exception instanceof LockedException){ //帳號鎖定 }else{ //其他錯誤 } result.setState(false); // 把result對象轉成 json 格式 字符串 通過 response 以application/json;charset=UTF-8 格式寫到響應里面去 response.setContentType("application/json; charset=utf-8"); PrintWriter out = response.getWriter(); out.write(result.toJsonWhenDataIsNull()); } } /** * 或者使用實現AuthenticationFailureHandler類的方法來定義 */ //@Component //public class CustomizeAuthenctiationFailureHandler implements AuthenticationFailureHandler { // @Override // public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { // ......// } //}
3.自定義登錄成功處理器
/** * @author Chase Meng * @description: 自定義登錄成功處理器 * @created on 2020/9/7 */ @Component public class CustomizeAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { Result result = new Result(); result.setCode(2000); result.setMsg("登錄成功!"); result.setState(true); Admin admin=new Admin(); result.setData("{\"username\" : \""+SecurityContextHolder.getContext().getAuthentication().getName()+"\"}");//響應數據攜帶用戶名 // 把result對象轉成 json 格式 字符串 通過 response 以application/json;charset=UTF-8 格式寫到響應里面去 httpServletResponse.setContentType("application/json; charset=utf-8"); PrintWriter out = httpServletResponse.getWriter(); out.write(result.toJsonWhenDataIsNull()); } } /** * 或者使用實現AuthenticationSuccessHandler類的方法來定義 */ /*@Component public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { ...... } }*/
4.屏蔽重定向的登錄頁面,並返回統一的json格式的數據
/** * @author Chase Meng * @description: 屏蔽重定向的登錄頁面,並返回統一的json格式的數據 * @created on 2020/9/7 */ @Component public class CustomizeAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { Result result=new Result(); httpServletResponse.setContentType("application/json; charset=utf-8"); result.setCode(1000); result.setMsg("沒有登錄權限,請先登錄!"); result.setState(false); PrintWriter out = httpServletResponse.getWriter(); out.write(result.toJsonWhenDataIsNull()); } }
5.自定義注銷成功處理器
/** * @author Chase Meng * @description: 自定義注銷成功處理器 * @created on 2020/9/7 */ @Component public class CustomizeLogoutSuccessHandler implements LogoutSuccessHandler { @Override public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { Result result=new Result(); httpServletResponse.setContentType("application/json; charset=utf-8"); result.setCode(1000); result.setMsg("注銷成功!"); result.setState(true); PrintWriter out = httpServletResponse.getWriter(); out.write(result.toJsonWhenDataIsNull()); httpServletResponse.getWriter().flush(); } }
6.自定義spring security配置
/** * @author Chase Meng * @Description 自定義spring security配置 * @Date Create in 2020/09/07 */ @EnableWebSecurity public class MySecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private CustomizeAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private CustomizeAuthenctiationFailureHandler myAuthenctiationFailureHandler; @Autowired private CustomizeAuthenticationEntryPoint myAuthenticationEntryPoint; @Autowired private CustomizeLogoutSuccessHandler logoutSuccessHandler; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { //配置攔截規則 //按先后注冊的先后順序匹配,因此順序要格外注意 http.authorizeRequests() .antMatchers("/upload/picture").permitAll() //放行圖片上傳接口 .antMatchers("/*d/**").authenticated() //攔截所有對回收站的查詢 .antMatchers(HttpMethod.GET).permitAll() //放行其他所有GET請求 .anyRequest().authenticated() //攔截其余請求 .and() .csrf().disable(); // 禁用跨站攻擊,否則允許通行的其他路徑的除了get請求之外的都會被攔截(包括登錄注銷接口) //開啟跨域 http.cors(); //開啟自動配置登錄 http.formLogin().permitAll() .successHandler(myAuthenticationSuccessHandler) //注冊自定義處理器 .failureHandler(myAuthenctiationFailureHandler) // .loginPage("http://localhost:8080/") //登錄頁(GET) .loginProcessingUrl("/user/login"); //登錄接口(POST) //記住密碼 // http.rememberMe(); //屏蔽Spring Security默認重定向登錄頁面以實現前后端分離功能 http.exceptionHandling() .authenticationEntryPoint(myAuthenticationEntryPoint);//匿名用戶訪問無權限資源時的異常 // .accessDeniedHandler(); //用來解決認證過的用戶訪問無權限資源時的異常 http.logout().permitAll() .logoutSuccessHandler(logoutSuccessHandler) //注冊登錄失敗處理器 .deleteCookies("JSESSIONID") //登出后刪除cookie .logoutUrl("/user/logout"); //登出接口(POST) } }
三.結果
數據庫中只有一個管理員:
username:admin
password:abc123
- 用戶不存在
- 密碼錯誤
- 登錄成功