SpringSecurity筆記2-自定義認證

自定義用戶

說明

  • 用戶名和密碼通常存儲於數據庫中,表單輸入的用戶名和密碼與數據庫查詢出來的用戶名和密碼進行比對
  • 如下例子爲SpringSecurity5.x

實現UserDetailsService接口

@Service
public class UserService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //此處根據用戶名在數據庫中查找,這裏再也不查找,直接返回一個org.springframework.security.core.userdetails.User對象(若是是自定義的User類,須要實現UserDetails接口)

        return new User(username,new BCryptPasswordEncoder().encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}
  • AuthorityUtils.commaSeparatedStringToAuthorityList() 方法模擬一個admin的權限,該方法能夠將逗號分隔的字符串轉換爲權限集合(理解)
  • 用戶認證流程
    從數據庫中查找用戶信息,若是爲空,則拋出異常,不然返回一個用戶對象,再由系統提供的DaoAuthenticationProvider類去比對密碼是否正確
  • 若是本身編寫User實體類,須要實現UserDetails 接口

編寫配置類

@Configuration
public class BrowserSecurity extends WebSecurityConfigurerAdapter {

    //引入自定義UserDetailsService
    @Autowired
    private UserService userService;

    //加密
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //配置內存認證
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //設置UserDetailsService以及密碼規則
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }
}

UserDetails接口簡介

自定義User類實現該接口,該接口主要有以下方法html

  • getAuthorities():獲取當前用戶對象的角色信息
  • getPassword(): 獲取密碼,與用戶輸入的密碼進行比對
  • getUsername: 獲取當前用戶對象的用戶名
  • isAccountNonExpired():當前用戶是否未過時,可自定義邏輯判斷,若是返回false,會拋出AccountExpiredException
  • isAccountNonLocked() :當前用戶是否未鎖定,可自定義邏輯判斷
  • isCredentialsNonExpired(): 當前帳戶密碼是否未過時
  • isEnabled(): 當前帳戶是否可用

自定義登陸頁

說明

  • 在表單登陸中通常是採用本身的登陸頁面
  • 須要在SpringSecurity中配置相關設置

一個簡單的登陸頁面

  • resources/static 目錄下編寫一個登陸頁面
    myLogin.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>自定義登陸頁面登陸</title>
</head>
<body>
<form action="/login" method="post">
    <div class="form">
        <h3>帳戶登陸</h3>
        <input type="text" placeholder="用戶名" name="username" required="required" /></br>
        <input type="password" placeholder="密碼" name="password" required="required" />
        <button type="submit">登陸</button>
    </div>
</form>
</body>
</html>

在自定義用戶的基礎上增長配置

總體配置以下:java

@Configuration
public class BrowserSecurity extends WebSecurityConfigurerAdapter {

    //引入自定義UserDetailsService
    @Autowired
    private UserService userService;

    //加密
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //配置內存認證
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //設置UserDetailsService以及密碼規則
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

    //配置HttpSecurity
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //受權配置
                .antMatchers("/myLogin.html").permitAll().anyRequest().authenticated()
                .and()
                //表單配置
                .formLogin().loginPage("/myLogin.html").loginProcessingUrl("/login")
                .and()
                //默認都會產生一個hiden標籤 裏面有安全相關的驗證 防止請求僞造 這邊咱們暫時不須要 可禁用掉
                .csrf().disable();
    }
}

結果

除了登陸頁面能夠直接訪問,其餘請求須要跳轉到登陸頁面完成認證後才能夠訪問web


自定義登陸成功和失敗

修改表單登陸配置

總體配置以下:spring

@Configuration
public class BrowserSecurity extends WebSecurityConfigurerAdapter {

    //引入自定義UserDetailsService
    @Autowired
    private UserService userService;

    //加密
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //配置內存認證
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //設置UserDetailsService以及密碼規則
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

    //配置HttpSecurity
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //受權配置
                .antMatchers("/myLogin.html").permitAll().anyRequest().authenticated()
                .and()
                //表單配置
                .formLogin().loginPage("/myLogin.html").loginProcessingUrl("/login")
                .successHandler(new AuthenticationSuccessHandler() {
                    //登陸成功返回一段json信息
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        //Authentication authentication 包含用戶登陸信息
                        String name = authentication.getName();
                        response.setContentType("application/json;charset=utf-8");
                        PrintWriter out = response.getWriter();
                        response.setStatus(200);
                        Map<String,Object> map = new HashMap<>();
                        map.put("status",200);
                        map.put("msg",name);
                        ObjectMapper mapper = new ObjectMapper();
                        out.write(mapper.writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    //登陸失敗,根據相關異常返回失敗信息
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        PrintWriter out = response.getWriter();
                        response.setStatus(401);
                        Map<String,Object> map = new HashMap<>();
                        map.put("status",401);
                        if(e instanceof LockedException){
                            map.put("msg","帳戶被鎖定");
                        }else if(e instanceof BadCredentialsException){
                            map.put("msg","帳戶名或密碼錯誤");
                        }else if(e instanceof DisabledException){
                            map.put("msg","帳戶被禁用");
                        }else if(e instanceof AccountExpiredException){
                            map.put("msg","帳戶已過時");
                        }else if(e instanceof CredentialsExpiredException){
                            map.put("msg","密碼已過時");
                        }else{
                            map.put("msg","登陸失敗");
                        }
                        ObjectMapper mapper = new ObjectMapper();
                        out.write(mapper.writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .and()
                //默認都會產生一個hiden標籤 裏面有安全相關的驗證 防止請求僞造 這邊咱們暫時不須要 可禁用掉
                .csrf().disable();
    }
}

也能夠自定義類實現接口的方式

  • 成功登陸:實現org.springframework.security.web.authentication.AuthenticationSuccessHandler接口的onAuthenticationSuccess方法
@Component
public class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(mapper.writeValueAsString(authentication));
    }
}
  • 登陸失敗: 實現
    org.springframework.security.web.authentication.AuthenticationFailureHandleronAuthenticationFailure方法
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException {
    }
}
  • 以後在配置類中引入
@Autowired
    private MyAuthenticationSucessHandler authenticationSucessHandler;

    @Autowired
    private MyAuthenticationFailureHandler authenticationFailureHandler;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    http.formLogin() // 表單登陸
            .successHandler(authenticationSucessHandler) // 處理登陸成功
            .failureHandler(authenticationFailureHandler) // 處理登陸失敗 
}

結果

  • 登陸成功
    數據庫

  • 登陸失敗
    json

相關文章
相關標籤/搜索