[SpringSecurity]之二:自定義登錄

前言:
在上一篇文章中,介紹了springSecurity框架搭建后,這個項目啟動后,默認是必須登錄后,才能進入其他頁面的。而這個登錄頁面是springSecurity自帶的登錄界面。在實際的開發(fā)過程中,我們要寫一個漂亮的登錄界面,這就需要自定義登錄頁面了,那么接下來,就介紹自定義登錄頁面如何實現(xiàn)。
本篇內(nèi)容包括:自定義登錄頁面,自定義成功攔截器,自定義失敗攔截器,自定義登錄表單名稱,action。

一、步驟

  1. 創(chuàng)建登錄界面login.html
  2. 創(chuàng)建路徑映射對應(yīng)的Controller 。
  3. 創(chuàng)建WebSecurityConfigurerAdapter 的子類WebSecurityConfigurer,用@Configuration注解該子類。
    • @Configuration中所有使用@Bean注解的方法都會被動態(tài)代理,一次調(diào)用該方法返回的都是同一個實例。
    • 并重寫里面的configure()方法。
    • 并創(chuàng)建passwordEncoder()方法。在方法上使用@Bean注解。
  4. 創(chuàng)建UserDetailsService的實現(xiàn)類MyUserDetails類中,用@Component注解。
    • 使用@Autowired,注入PasswordEncoder對象 。
    • 重寫認證根據(jù)用戶名認證用戶的方法loadUserByUsername()
    • 設(shè)置用戶的密碼,角色。
  5. WebSecurityConfigurerAdapter 的子類中WebSecurityConfigurer,注入UserDetailsService的實現(xiàn)類,并在config()方法中配置。
  6. 分別創(chuàng)建自定義登錄成功,失敗處理器,對應(yīng)實現(xiàn)AuthenticationSuccessHandler,AuthenticationFailureHandler接口。使用@Component將它們放入容器中。以備WebSecurityConfigurerAdapter 的子類使用。
  7. WebSecurityConfigurerAdapter 的子類中WebSecurityConfigurer,注入 AuthenticationSuccessHandler,AuthenticationFailureHandler接口的處理器實現(xiàn)類,并在config進行配置。
  8. application.properties中配置視圖映射 。

注意:springboot使用的是使用thymeleaf的html5模板。

查看項目結(jié)構(gòu)示意圖。

表結(jié)構(gòu).png

二、清單說明

清單一、springboot是使用thymeleaf的html5模板。

步驟1.1中創(chuàng)建login.html頁面,頭部<html>中,標簽中引入<html lang="en" xmlns:th="http://www.thymeleaf.org">

清單二、springboot約定的資源文件映射路徑

springboot約定的資源文件映射路徑:
gradle項目資源目錄:src/main/java/resources
spring-boot項目靜態(tài)文件目錄:/src/main/java/resources/static
spring-boot項目模板文件目錄:/src/main/java/resources/templates


步驟1.8中配置application.properties中配置模板視圖映射。springboot因為已經(jīng)默認約定好了資源文件映射路徑。其實在這里可以不用配置。我還是要把它,貼在這里,一邊更好的來理解。

#視圖 這是默認的配置
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

#檢查模板是否存在,然后再呈現(xiàn)
spring.thymeleaf.check-template-location=true
清單三 、spring-security登錄界面的name。

spring-security中默認設(shè)置的用戶登錄表單中的name名稱為usernamepassword

默認設(shè)置的登錄表單中name名稱,用戶名密碼。同時還默認設(shè)置了登錄頁面表單請求的action=/login,以及必須為post方式請求。可以在spring-security源碼中查看:UsernamePasswordAuthenticationFilter。

【擴展閱讀】節(jié)選源碼片段一:UsernamePasswordAuthenticationFilter 定義的登錄名單中name名稱
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    private String usernameParameter = "username";
    private String passwordParameter = "password";
    private boolean postOnly = true;

    public UsernamePasswordAuthenticationFilter() {
        super(new AntPathRequestMatcher("/login", "POST"));
    }
    // ... ... 

}
清單四 、login.html。
<div align="center">
    <h2>自定義登錄頁面</h2>
    <form th:action="@{/login}" method="post">
        <label>
            <span>用戶名:</span>
            <input type="text" name="username">
            <!--<input type="text" name="uname">-->
        </label>
        <br/>
        <label>
            <span>密碼:</span>
            <input type="password" name="password">
            <!--<input type="password" name="psd">-->
        </label>
        <input type="submit" class="submit input_button"  value="登錄">
    </form>
</div>

注意
【要點1】form 表單提交的action 是login,及和登錄頁面同名(和路徑映射的controller同名)。

  1. 如果你改成其他的action,比如說為:/login/form,則需要在WebSecurityConfigurerAdapter 的子類
    的 config()方法中添加.loginProcessingUrl("/login/form") //form 表單action
    2.如果form表單的action如果和登錄頁面同名(和路徑映射的controller同名)【不一定非得是login】,那么無需添加.loginProcessingUrl("")這一句,它會默認請求。
    3.以上1,2兩種方式,action的值是否和請求頁面路徑的controller映射一致,都不需要去寫action對應(yīng)路徑的controller路徑。

【要點2】form表單中用戶名密碼,自定義
1.需要在WebSecurityConfigurerAdapter 的子類
的 config()方法中添加 .usernameParameter("uname").passwordParameter("psd")。

清單五、路徑映射的controller

步驟1.2 創(chuàng)建路徑映射對應(yīng)的Controller 。

@Controller
public class LoginController {
    @GetMapping("/login")
    public String login() {
        return "login";
    }
}
清單六、創(chuàng)建失敗,成功處理器

步驟:1.6 分別創(chuàng)建自定義登錄成功,失敗處理器,對應(yīng)實現(xiàn)AuthenticationSuccessHandler,AuthenticationFailureHandler接口。使用@Component將它們放入容器中。以備WebSecurityConfigurerAdapter 的子類使用。

代碼1:失敗處理器
@Component
public class FailureAuthenticationHandler implements AuthenticationFailureHandler {
    protected Logger logger=LoggerFactory.getLogger(getClass());
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        logger.info("登錄失敗");
        httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        httpServletResponse.getWriter().write(objectMapper.writeValueAsString(e));
//        httpServletResponse.getWriter().write(objectMapper.writeValueAsString(new SimpleResonse(e.getMessage())));
    }
}

代碼2:成功處理器
@Component
public class SuccessAuthenticationHandler implements AuthenticationSuccessHandler {
    protected Logger logger=LoggerFactory.getLogger(getClass());
    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        logger.info("登錄成功");
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication)+"/n"+"登錄成功了");
    }
}

清單七、創(chuàng)建用戶信息:定義登錄密碼

步驟:1.4 創(chuàng)建UserDetailsService的實現(xiàn)類 MyUserDetails。

@Component
public class MyUserDetails implements UserDetailsService  {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String password=passwordEncoder.encode("123456");
        return new User(username,password,AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

清單八、創(chuàng)建WebSecurityConfigurerAdapter的子類 WebSecurityConfigurer
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Autowired
    private FailureAuthenticationHandler failureHandler;

    @Autowired
    private SuccessAuthenticationHandler successHandler;
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       // 自定義login.html登錄界面
        http.formLogin().loginPage("/login")  
                // .loginProcessingUrl("/login) //form 表單action
                .failureHandler(failureHandler)
                .successHandler(successHandler)
            //.usernameParameter("uname")
                //.passwordParameter("psd")
                .and()
                .authorizeRequests()
                .antMatchers("/login").permitAll() 
                // 除了login.html頁面以外都需要身份認證 (一定要記得添加這一句,否則就是死循環(huán))
                .anyRequest()
                .authenticated()
                ;
    }

  
}

注意
【要點1】:一定要把自定義登錄界面給放開權(quán)限。
.antMatchers("/login").permitAll(),否則就是死循環(huán)。

最后運行項目 ,
登錄:用戶名,隨便輸入,密碼為:123456
登錄成功后,會顯示

{"authorities":[{"authority":"admin"}],"details":{"remoteAddress":"127.0.0.1","sessionId":"7BD95EAEB7B20BDAA5CCB18C0C601684"},"authenticated":true,"principal":{"password":null,"username":"2222","authorities":[{"authority":"admin"}],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true},"credentials":null,"name":"2222"}/n登錄成功了

登錄失敗了,會顯示

{"cause":null,"stackTrace":[{"methodName":"additionalAuthenticationChecks","fileName":"DaoAuthenticationProvider.java","lineNumber":89,"className":"org.springframework.security.authentication.dao.DaoAuthenticationProvider","nativeMethod":false},{"methodName":"authenticate","fileName":"AbstractUserDetailsAuthenticationProvider.java","lineNumber":166,"className":"org.springframework.security.authentication.dao.AbstractUserDetailsAuthentication
Provider","nativeMethod":false},
// ... ... 省略好多內(nèi)容
{"methodName":"run","fileName":"Thread.java","lineNumber":748,"className":"java.lang.Thread","nativeMethod":false}],"localizedMessage":"壞的憑證","message":"壞的憑證","suppressed":[]}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,236評論 3 119
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,351評論 25 708
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong閱讀 22,970評論 1 92
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評論 19 139
  • 本書和作者 當當網(wǎng)對這本書的推薦語:杰出不是一種天賦,而是一種人人都可以學會的技巧!迄今發(fā)現(xiàn)的最強大學習法,成為任...
    趙秀麗閱讀 723評論 0 1

友情鏈接更多精彩內(nèi)容