Spring MVC + Security 4 初體驗(Java配置版)

來源:互聯網
上載者:User

標籤:filters   head   相關   app   dao   addm   val   sha   文章   

這篇文章同樣是使用的Java配置,而非XML配置,如果你對於Java配置的Spring MVC開發還不太熟悉,可以先看我這篇文章。

轉自:http://xueliang.org/article/detail/20170302232815082

Authority

建立一個 Authority ,實現自 org.springframework.security.core.GrantedAuthority 類,getAuthority 方法只返回一個表示許可權名稱的字串,如 AUTH_USER 、 AUTH_ADMIN 、 AUTH_DBA 等。

複製
public class Authority implements GrantedAuthority {    private static final long serialVersionUID = 1L;    private String authority;    public Authority() {  }    public Authority(String authority) {        this.setAuthority(authority);    }    @Override    public String getAuthority() {        return this.authority;    }    public void setAuthority(String authority) {        this.authority = authority;    }}
User

User 類實現自 org.springframework.security.core.userdetails.UserDetails 介面,包含一組許可權的集合 authorities

複製
public class User implements UserDetails {    private static final long serialVersionUID = 1L;    private String username;    private String password;    private List<Authority> authorities;    @Override    public String getUsername() {        return username;    }    public void setUsername(String username) {        this.username = username;    }    @Override    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    @Override    public Collection<? extends GrantedAuthority> getAuthorities() {        return this.authorities;    }    public void setAuthorities(List<Authority> authorities) {        this.authorities = authorities;    }    @Override    public boolean isAccountNonExpired() {        return true;    }    @Override    public boolean isAccountNonLocked() {        return true;    }    @Override    public boolean isCredentialsNonExpired() {        return true;    }    @Override    public boolean isEnabled() {        return true;    }}
UserDetailsService

MyUserDetailsService 實現了 org.springframework.security.core.userdetails.UserDetailsService 的 loadUserByUsername 方法,該方法根據使用者名稱查詢合格使用者,若沒有找到合格使用者,必須拋出 UsernameNotFoundException 異常,而不能返回空。這裡可以調用 DAO 層,從資料庫查詢使用者,我為了簡單,直接將使用者臨時放到一個常量內,類比從資料庫查詢使用者。

複製
@Servicepublic class MyUserDetailsService implements UserDetailsService {    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        List<User> userList = Constants.userList;        for (int i = 0, len = userList.size(); i < len; i++) {            User user = userList.get(i);            if (user.getUsername().equals(username)) {                return user;            }        }        throw new UsernameNotFoundException("使用者不存在!");    }}
SecurityConfig

SecurityConfig 類繼承org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapterWebSecurityConfigurerAdapter提供了一些預設的配置,方便建立一個執行個體。

進入 configure 方法中,首先允許任何情況下的對csrfTokenApi 的請求,該 API 返回一個 csrfToken ,預設情況下除GETHEADTRACE 和 OPTIONS外,所有請求都必須經過 CSRF 認證。接下來對不同的API請求設定不同的許可權,並且確保所有對/api/ 下的請求都經過了認證。 
這裡向 access 方法傳遞的運算式中的許可權名稱,對應上面提到的 Authority 類中 getAuthority 返回的字串的值,詳細的運算式介紹,請移步至這裡。

接著,對登入表單進行配置。通過 loginProcessingUrl 配置表單提交地址,這個地址對應的API不需要自己寫,Spring Security 會自動攔截提交到此地址請求,將其視為登入請求。如果希望登入成功後通過伺服器轉寄到其他頁面,可以調用 successForwardUrl(String forwardUrl) 方法指定跳轉的地址,對應地,指定失敗後跳轉地址的方法是 failureForwardUrl(String forwardUrl)

這裡我使用了RESTful,故不需要佈建服務端的轉寄,而是配置了另外兩處:successHandler 和 failureHandler ,successHandler 方法接收一個 AuthenticationSuccessHandler 對象,認證通過之後,Spring Security 將調用該對象的 onAuthenticationSuccess 方法,類似地,failureHandler 方法接收一個 AuthenticationFailureHandler 對象,認證失敗之後,將調用該對象的onAuthenticationFailure 方法。

配置完登入相關資訊之後,接著配置和登出有關的資訊。和配置登入表單提交地址類似,這裡需要配置登出請求提交地址,這裡調用logoutUrl 方法,指定登出的連結地址,該地址和前面提到的 loginProcessingUrl 都不需要自己寫,這兩個都是全權交由 Spring Security 來處理。當使用者請求 logoutUrl 方法指定的地址時,Spring Security 將對使用者執行登出操作。和前面提到的successForwardUrl 類似,這裡提供了 logoutSuccessUrl 方法指定登出成功之後轉寄的地址。不過我用了RESTful,就不再調用此方法,而是調用 logoutSuccessHandler 傳入 LogoutSuccessHandler 對象,登出成功後將調用該對象的 onLogoutSuccess 方法。

最後,配置對異常的處理 exceptionHandling ,和上面介紹的 successHandler 、 failureHandler 以及 logoutSuccessHandler 差不多,authenticationEntryPoint 接收一個 AuthenticationEntryPoint 對象,當使用者請求的操作需要登入時,將拋出AuthenticationException 異常,並且將該異常傳入到 AuthenticationEntryPoint 對象的 commence 方法。 
accessDeniedHandler 方法接收一個 AccessDeniedHandler 對象,該對象的 handle 方法將在許可權不足時調用。

配置完這些,看 configureGlobalSecurity 方法,給 AuthenticationManagerBuilder 配置一個 UserDetailsService 對象,當使用者執行登入時,Spring Security 將調用該對象的 loadUserByUsername 方法,將 username 傳入此方法,根據 username 擷取一個UserDetails 對象。

另外,由於不能在資料庫中儲存純文字密碼,這裡對密碼進行 bcrypt 加密後儲存,驗證密碼是否正確時,需要對使用者輸入的純文字密碼進行bcrypt 加密後比較密文是否一致,故這裡需要提供一個 BCryptPasswordEncoder 對象。

複製
@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Value("${api.csrftoken}")    private String csrfTokenApi;    @Value("${api.login}")    private String loginApi;    @Value("${api.logout}")    private String logoutApi;    @Autowired    private MyUserDetailsService userDetailsService;    @Autowired    private PasswordEncoder passwordEncoder;    @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests().antMatchers(csrfTokenApi).permitAll()        .antMatchers("/api/user/**").access("hasAuthority(‘USER‘)")        .antMatchers("/api/admin/**").access("hasAuthority(‘ADMIN‘)")        .antMatchers("/api/dba/**").access("hasAuthority(‘DBA‘)")        .antMatchers("/api/**").fullyAuthenticated()        .and().formLogin().loginProcessingUrl(loginApi)        .successHandler(new RestAuthenticationSuccessHandler())        .failureHandler(new RestAuthenticationFailureHandler())        .and().logout().logoutUrl(logoutApi)        .logoutSuccessHandler(new RestLogoutSuccessHandler())        .and().exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint())        .accessDeniedHandler(new RestAccessDeniedHandler());    }    @Autowired    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);    }    @Bean    public PasswordEncoder passwordEncoder() {        return new BCryptPasswordEncoder(11);    }}
WebAppConfig

因為採用RESTful風格,這裡配置響應視圖為json格式。

複製
@Configuration@EnableWebMvc@ComponentScan(basePackages = "org.xueliang.springsecuritystudy")@PropertySource({"classpath:config.properties"})public class WebAppConfig extends WebMvcConfigurerAdapter {    @Bean    public RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Autowired MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter, @Autowired ContentNegotiationManager mvcContentNegotiationManager) {        RequestMappingHandlerAdapter requestMappingHandlerAdapter = new RequestMappingHandlerAdapter();        requestMappingHandlerAdapter.setMessageConverters(Collections.singletonList(mappingJackson2HttpMessageConverter));        requestMappingHandlerAdapter.setContentNegotiationManager(mvcContentNegotiationManager);        return requestMappingHandlerAdapter;    }    @Bean    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {        return new MappingJackson2HttpMessageConverter();    }    /**     * 設定歡迎頁面     * 相當於web.xml中的 welcome-file-list > welcome-file     */    @Override    public void addViewControllers(ViewControllerRegistry registry) {        registry.addRedirectViewController("/", "/index.html");    }}
WebAppInitializer

Spring Security 架構是完全基於標準的 Servlet 過濾器的,這裡我們需要在 WebInitializer 中引入 DelegatingFilterProxy 過濾器。

複製
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {    @Override    public void onStartup(ServletContext servletContext) throws ServletException {        servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain")).addMappingForUrlPatterns(null, false, "/api/*");        // 靜態資源映射        servletContext.getServletRegistration("default").addMapping("*.html", "*.ico");        super.onStartup(servletContext);    }    @Override    protected Class<?>[] getRootConfigClasses() {        return new Class[] { WebAppConfig.class };    }    @Override    protected Class<?>[] getServletConfigClasses() {        return null;    }    @Override    protected String[] getServletMappings() {        return new String[] { "/" };    }    @Override    protected Filter[] getServletFilters() {        return new Filter[] { new CharacterEncodingFilter("UTF-8", true) };    }}
Source

本文使用到的項目源碼已經放到 Github 上,你可以下載後運行。

Spring MVC + Security 4 初體驗(Java配置版)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.