角色與授權


在〈自訂登入登出頁面〉只使用了 authenticated 方法,這表示只要是登入成功,就可以在 Web 應用程式中暢行無阻,然而,Web 應用程式中也許某些頁面只允許站長存取,有些資源只允許會員使用呢?

可以使用 hasRolehasAnyRole 等方法,指定某些頁面可以存取的角色,例如,目前 SecurityConfig 中有 ADMINMEMBER 兩種角色,就用這兩個角色來玩點權限設定吧!

package cc.openhome.web;

... 略

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder pwdEncoder = new BCryptPasswordEncoder();
        auth.inMemoryAuthentication() 
            .passwordEncoder(pwdEncoder)
                .withUser("admin") 
                    .password(pwdEncoder.encode("admin12345678"))
                    .roles("ADMIN", "MEMBER")
            .and()
                .withUser("caterpillar")
                    .password(pwdEncoder.encode("12345678"))
                    .roles("MEMBER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/admin").hasRole("ADMIN")  
            .antMatchers("/member").hasAnyRole("ADMIN", "MEMBER")
            .antMatchers("/user").authenticated()
            .anyRequest().permitAll()
            .and()
            .formLogin() 
                .loginPage("/login_page")
                .loginProcessingUrl("/perform_login")
                .failureUrl("/login_page?error")
            .and()
            .logout()
                .logoutUrl("/perform_logout")
                .logoutSuccessUrl("/login_page?logout");
    } 
}

如上設定之後,登入的使用者必須有 ADMIN 角色才可以存取 /admin,而 MEMBER 角色才可以存取 /member/user 只需要登入就可以看。

如果登入為 caterpillar,卻想要觀看 /admin 的話,就會出現 403 – Forbidden 的頁面。

roles 方法設定的角色名稱,實際上都會被加上 ROLE_ 前置名稱,在設定授權時,使用 hasAuthorityhasAyAuthority 方法,就必須使用 ROLE_XXX。例如:

... 略
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/admin").hasAuthority("ROLE_ADMIN")
        .antMatchers("/member").hasAuthority("ROLE_MEMBER")
        .antMatchers("/user").authenticated()
        .anyRequest().permitAll()
        .and()
        .formLogin() 
            .loginPage("/login_page")
            .loginProcessingUrl("/perform_login")
            .failureUrl("/login_page?error")
        .and()
        .logout()
            .logoutUrl("/perform_logout")
            .logoutSuccessUrl("/login_page?logout");
} 
... 略

你也可以使用 access 指定 Spring EL,實現更靈活的配置,例如:

...
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/admin").access("hasRole('ADMIN') and hasIpAddress('192.168.8.100')")
        .antMatchers("/member").access("hasAnyRole('ADMIN', 'MEMBER')")
        .antMatchers("/user").authenticated()
        .anyRequest().permitAll()
        .and()
        .formLogin() 
            .loginPage("/login_page")
            .loginProcessingUrl("/perform_login")
            .failureUrl("/login_page?error")
        .and()
        .logout()
            .logoutUrl("/perform_logout")
            .logoutSuccessUrl("/login_page?logout");
} 
...

有關於 access 與 Spring EL 的搭配,可以參考〈Expression-Based Access Control〉。

你可以在 RoleAuthorization 找到以上的範例專案。