1. 程式人生 > 實用技巧 >Spring Security(1)--- 許可權控制基本功能實現

Spring Security(1)--- 許可權控制基本功能實現

前言

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頁面,提示沒有許可權,這說明我們的配置是正確的。

附件列表