SpringSecurity實現用戶認證授權系統

本文同步發佈在是與非博客(www.zhuoerhuobi.cn)

隨着是與非博客基礎功能的完善,給我的網站加上用戶認證授權系統這件事終於是提上日程了。

用戶系統我們可以拆解爲登錄認證和授權訪問這兩個功能。我們先登陸賬號,用戶的狀態就存在在網站了,然後網站再根據該用戶的角色權限授權該用戶訪問某些頁面和接口。

如果我們自己動手實現用戶系統,可以料想到大致流程爲:
讀取用戶輸入的賬戶密碼 --> 加密後和數據庫中的用戶表進行比對 --> 若比對成功,則讀取數據庫中用戶的信息,登陸成功 --> 在我們已有的接口或頁面上添加判斷條件或攔截器,根據用戶信息中的角色或者權限等字段判斷是否讓用戶通行。

這麼做的缺點也很明顯,工作量大,並且認證系統和我們的業務代碼高度耦合,不利於後期維護。

在這種情況下,用戶系統相關框架應運而生,當下最流行的shrio和springsecurity都能很好的完成我們上面的需求,並且它們遵循了aop原則,並不需要更改我們的業務代碼就可以加入用戶系統功能,相當方便。

要引入springsecurity功能,我們首先要在pom文件中添加依賴:

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

這樣,項目就引入了security模塊。security默認攔截所有請求,而默認登錄用戶名爲user,默認密碼在服務啓動時會打印在終端下。

security相關配置繼承自WebSecurityConfigurerAdapter類,所以我們定義自己的MySecurityConfig類繼承WebSecurityConfigurerAdapter,並重寫其中幾個方法。


@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    MyUserDetailService myUserDetailService;

    @Override
    public void configure(WebSecurity web) throws Exception {
		//不攔截靜態資源
        web.ignoring().antMatchers("/css/**","/js/**","/fonts/**","/lib/**","/img/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();/*關閉防禦csrf攻擊功能*/
		//鏈式編程,可以連寫若干個功能,爲了方便理解,我們還是拆開來寫。
        http.formLogin() /*開啓表單登錄*/
            .loginPage("/login")/*自定義我們自己的登陸頁面*/
            .defaultSuccessUrl("/index?page=1").permitAll()/*自定義登錄成功頁面*/
            .and().authorizeRequests()
            .antMatchers("/login","/resources/**").permitAll()/*選擇哪些頁面不需要權限*/
            .antMatchers("/admin/**").hasRole("ADMIN");/*選擇哪些頁面必須要相應角色*/
			
        http.logout().logoutSuccessUrl("/");/*開啓註銷功能*/
        
        http.rememberMe().rememberMeParameter("rememberMe");/*開啓記住我功能,這裏的參數要和前端相應的參數name相同*/
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		//認證規則裏的用戶從我們自定義的MyUserDetailService裏讀取,並用BCryptPasswordEncoder加密
        auth.userDetailsService(myUserDetailService).passwordEncoder(new BCryptPasswordEncoder());
    }

}

其中我們自定義的MyUserDetailService從數據庫中讀取用戶信息:


@Service
public class MyUserDetailService implements UserDetailsService {
    @Autowired
    MyUserMapper myUserMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        MyUser user = myUserMapper.getUserByName(username);
        if (user != null) {
            return new User(user.getName(),user.getPassword(),AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole()));
        }
        else {
            throw new UsernameNotFoundException("用戶\""+username+"\"不存在!");
        }
    }
}

爲了給用戶賦予角色,我們還要在用戶表添加role字段,注意role要有ROLE_ 的前綴。

接下來我們來完成前端部分,其中用戶名輸入框的name屬性設置爲username,密碼輸入框設置爲password

<form class="form-horizontal" action="/login" method="post">
	<span class="heading">用戶登錄</span>
	<div class="form-group">
		<input type="text" name="username" class="form-control" id="inputEmail3" placeholder="用戶名或電子郵件">
		<i class="fa fa-user"></i>
	</div>
	<div class="form-group help">
		<input type="password" name="password" class="form-control" id="inputPassword3" placeholder="密 碼">
		<i class="fa fa-lock"></i>
		<a href="#" class="fa fa-question-circle"></a>
	</div>
	<div class="form-group">
		<div class="main-checkbox">
			<input type="checkbox" id="checkbox1" name="rememberMe"/>
			<label for="checkbox1"></label>
		</div>
		<span class="text">Remember me</span>
		<button type="submit" class="btn btn-default">登錄</button>
	</div>
</form>

由於前端我們使用的是thymeleaf,而thymeleaf和security是有合作的包,所以我們再導入一個依賴方便我們操作前端:

<dependency>
	<groupId>org.thymeleaf.extras</groupId>
	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
	<version>3.0.4.RELEASE</version>
</dependency>

引入後我們就可以以sec:前綴來書寫動態的html標籤,類似於th:

<div class="pull-right login-nav">
	<ul>
		<li class="dropdown" sec:authorize="isAuthenticated()"> <a sec:authentication="name" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" style="color: white"><span class="caret"></span></a>
			<ul class="dropdown-menu dropdown-menu-left">
				<li sec:authorize="hasRole('ADMIN')"><a title="進入後臺管理系統" href="/admin/article">進入後臺</a></li>
				<li><a title="查看或修改個人信息" data-toggle="modal" data-target="#seeUserInfo">個人信息</a></li>
				<li><a title="查看您的登錄記錄" data-toggle="modal" data-target="#seeUserLoginlog">登錄記錄</a></li>
			</ul>
		</li>
		<li sec:authorize="isAuthenticated()"><a href="/logout" onClick="if(!confirm('是否確認退出?'))return false;"><button class="btn-login">退出登錄</button></a></li>
		<li sec:authorize="!isAuthenticated()"><a href="/login"><button class="btn-login" href="/login">請登錄</button></a></li>
	</ul>
</div>

這樣,我們就實現了簡單的登錄認證功能,並對後臺的接口和頁面加以限制,只有管理員用戶可以訪問。具體效果可以在是與非博客進行體驗。


這篇文章只是簡單記錄了我的springsecurity使用過程,其中還有很多坑和源碼相關知識。如果看了這篇博文後你還想深入瞭解security的原理,請上網百度,或者我自己研究明白了或許會再出一篇文章專門講解(不大可能)。

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