Spring Security系列一 權限控制基本功能實現

前言

Spring Securityacegi進化而來,是一個安全權限管理框架,功能十分的強大。

但也正是因爲功能強大,使用起來就變的非常的麻煩,至少個人感覺很煩很煩,甚至覺得Spring Security是不是應該爲常規的Java web應用出一個簡化版?相對而言Shiro就清爽很多,當然這裏不討論誰好誰壞,能解決項目的問題就好。

官方給出的示例中(包括網上一搜就找到的一堆資料)是不使用數據庫的,所有的權限配置都寫死在配置文件和代碼中,這在實際項目中顯然是很難滿足的,難道老外的權限需求真的如此簡單麼?

而想要實現權限的動態可配,友好的提示信息等,這些都需要自己去實現,這實現的過程還是很煩鎖的,特別是對Spring Security還不是很熟的情況下。

目前網上的文章大多都是用xml配置來實現的,本文將全部使用JavaConfig的方式,也不會過多的講解Spring Security的內容,重在使用,能滿足當前項目的需求就好。

下面以一個最簡單的示例開始。

添加依賴

maven項目,第一步依然是添加我們需要的依賴。

在這個示例中,只是簡單的演示,並沒有涉及到數據庫,所以暫時只需要這些。嗯,另外模板引擎換成了thymeleaf,不再是我以前一直使用的velocity了,因爲我發現thymeleaf有些地方更好用一些。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
</dependencies>

系統權限設計

在本示例中,有以下幾個頁面,區分不同的權限:

  • 首頁 所有人都可以訪問
  • 登錄頁 所有人可都可以訪問
  • 歡迎頁 登錄後的用戶都可以訪問
  • 管理頁 只有管理員可訪問
  • 無權限提醒頁 當一個用戶的訪問沒有權限時,跳轉到該頁

確定了以上頁面,接下來就是建立相應的用戶了。

建立用戶對象

爲了簡單起見,我們的用戶只需要用戶名、密碼以及一個對應的角色。

public class User {

    private String username;

    private String password;

    private String role;

}

用戶登錄數據層

這裏我們並沒有真正的數據層,只是建立幾個模擬用戶數據:

public class UserDaoImpl implements UserDao {

    private static final Map<String, User> userMap = new HashMap<String, User>();

    static {

        User user = new User();
        user.setUsername("liyd");
        user.setPassword("123456");
        user.setRole("user");
        userMap.put(user.getUsername(), user);

        user = new User();
        user.setUsername("admin");
        user.setPassword("123456");
        user.setRole("admin");
        userMap.put(user.getUsername(), user);
    }

    @Override
    public User getUser(String username) {
        return userMap.get(username);
    }
}

前端展現

在展現層中,我們需要前面提到的幾個頁面,並增加一個Controller,代碼如下:

@Controller
public class UserController {

    @RequestMapping(value = { "", "/index" }, method = RequestMethod.GET)
    public String home() {
        return "index";
    }

    @RequestMapping(value = "/user-page", method = RequestMethod.GET)
    public String userPage() {
        return "user-page";
    }

    @RequestMapping(value = "/admin-page", method = RequestMethod.GET)
    public String adminPage() {
        return "admin-page";
    }

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login() {
        return "login";
    }

    @RequestMapping("/403")
    public String forbidden() {
        return "403";
    }
}

可以看到都是簡單的跳轉到相應頁面,所有的頁面都在resources/templates下,這個就不細講了。

從上面可以看出/login只是做了一個登錄頁跳轉,但是具體登錄的驗證邏輯卻沒有,這是因爲Spring Security要求使用者將此塊的功能必須委託給它來處理。

另外/403實際上是用戶訪問沒有權限時跳轉的頁面,Spring Security會設置此時的http狀態碼爲403,因此我們需要設置一個錯誤頁處理,當發現http狀態碼爲403時跳轉到/403處理。

@Configuration
public class WebAppConf extends WebMvcConfigurerAdapter {

    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer() {

        return new EmbeddedServletContainerCustomizer() {

            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {

                ErrorPage error403Page = new ErrorPage(HttpStatus.FORBIDDEN, "/403");

                container.addErrorPages(error403Page);
            }
        };
    }
}

添加權限驗證

到上面那一步,系統功能已經差不多了,但是還缺少權限驗證的配置。

其實權限控制從你向maven的pom.xml中添加spring-boot-starter-security依賴開始就已經起作用了,

如果這時候你啓動項目訪問的話,會發現Spring Security已經將所有請求攔截並自動生成了一個登錄框讓你登錄。

但顯然這個登錄框你是無法登錄成功的,因爲後臺具體登錄的邏輯我們還沒有完成。

建立自定義的UserDetailsService

Spring Security的用戶信息獲取最終是通過UserDetailsServiceloadUserByUsername方法來完成的,這個後面會細講,這裏先做了解。

根據上面的UserDao實現,我們建立自定義的CustomUserDetailService,至於角色的前綴,我記得Spring Security 3.2.x版本是不需要你手動再加的,

這裏我用的是Spring Boot 1.3.3,Spring Security版本爲4.0.3,不知道爲什麼又要加上了,看AffirmativeBased裏面的源代碼調試,確實是一個有前綴一個沒前綴,搞不懂Spring Security走的什麼路子。

public class CustomUserDetailsService implements UserDetailsService {

private static Map<String, User> userMap = new HashMap<String, User>();

    static {
        User user = new User("admin", "123456", "admin");
        userMap.put(user.getUsername(), user);
        user = new User("selfly", "123456", "user");
        userMap.put(user.getUsername(), user);

    }

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userMap.get(s);
        if (user == null) {
            throw new UsernameNotFoundException("not found");
        }
        List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getRole()));
        LOG.info("username:{},role:{}", user.getUsername(), user.getRole());
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                authorities);
    }

}

配置Security

接下來就是配置Spring Security了,我們建立一個類SecurityConf,使用JavaConfig的方式,指定AuthenticationManager使用我們自己的CustomUserDetailsService來獲取用戶信息,並設置首頁、登錄頁等。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConf extends WebSecurityConfigurerAdapter {

    @Bean
    public UserDetailsService userDetailsService() {
        return new CustomUserDetailsService();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/", "/index")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/user-page")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }
}

可以看到SecurityConf上添加了@EnableWebSecurity註解用來跟Spring mvc集成。同時它還繼承了WebSecurityConfigurerAdapter類用來重寫我們需要的配置。

添加角色權限驗證

上面已經完成了系統的登錄和驗證功能,但並沒有進行權限的區分,要怎麼樣把普通用戶和管理用戶區分開呢?

很簡單,只需要增加@PreAuthorize註解即可。修改UserController,對/user/admin分別添加註解:

@RequestMapping(value = "/user", method = RequestMethod.GET)
@PreAuthorize("hasAnyRole('admin', 'user')")
public String userPage() {
    return "user-page";
}

@RequestMapping(value = "/admin", method = RequestMethod.GET)
@PreAuthorize("hasAnyRole('admin')")
public String adminPage() {
    return "admin-page";
}

啓動項目

現在,可以啓動項目,訪問http://localhost:8080,根據提示來登錄檢查一下權限是否正確。

當使用admin/123456登錄時,所有的頁面都是允許訪問的。

當使用selfly/123456登錄時,發現訪問/admin時跳到了/403頁面,提示沒有權限,這說明我們的配置是正確的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章