SpringSecurity學習之基於數據庫的用戶認證


  SpringSecurity給我們提供了一套最基本的認證方式,可是這種方式遠遠不能滿足大多數系統的需求。不過好在SpringSecurity給我們預留了許多可擴展的接口給我們,我們可以基於這些接口實現自己的認證方式。

一、前期准備工作

1.1、創建示例數據庫

Student表:

create table student
(
    id int auto_increment
        primary key,
    stuName varchar(8) null,
    password varchar(16) null,
    joinTime datetime null,
    clz_id int null
)
;

Classes(班級)表:

create table classes
(
    id int auto_increment
        primary key,
    clz_name varchar(16) not null
)
;

 

1.2、添加相關依賴

compile group: 'mysql', name: 'mysql-connector-java'
compile group: 'org.springframework.security', name: 'spring-security-taglibs'
compile('org.springframework.boot:spring-boot-starter-jdbc')

 

 

二、實現步驟

2.1  定義Student類

package com.bdqn.lyrk.security.study.app.pojo;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.sql.Timestamp;
import java.util.Collection;

public class Student extends User {

    private Timestamp joinTime;

    public Timestamp getJoinTime() {
        return joinTime;
    }

    public void setJoinTime(Timestamp joinTime) {
        this.joinTime = joinTime;
    }

    public Student(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }

}
View Code

  在這里定義的類繼承User,User是SpringSecurity里的一個類,用以描述一個用戶和其最基本的屬性,當然我們要擴展它的用戶我們也可以實現UserDetails接口

2.2 實現UserDetailsService

package com.bdqn.lyrk.security.study.app.service;

import com.bdqn.lyrk.security.study.app.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class UserService implements UserDetailsService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User.UserBuilder users = User.withDefaultPasswordEncoder();
        Map<String, Object> map = jdbcTemplate.queryForMap("select t.clz_name,t1.stuName,t1.password,t1.joinTime from student t1 inner  join classes t on t.id = t1.clz_id where stuName = ?", username);
        Timestamp joinTime = null;
        if (map != null && map.size() > 0) {
            String stuName = (String) map.get("stuName");
            String password = (String) map.get("password");
            joinTime = (Timestamp) map.get("joinTime");
            String clzName = (String) map.get("clz_name");
            users.password(password);
            users.username(stuName);
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(clzName);
            List<GrantedAuthority> list = new ArrayList<>();
            list.add(authority);
            users.authorities(list);
        }
        UserDetails userDetails = users.build();
        Student student = new Student(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
//        UserDetails userDetails = User.withDefaultPasswordEncoder().
        student.setJoinTime(joinTime);
        return student;
    }
}
View Code

  在這個接口里我們要實現根據用戶名查找用戶的方法,那么一般情況下我們都會根據自己系統的用戶表來獲取用戶信息,這里面注意幾個方面:

  1)需要設置PasswordEncoder 

  2) 需要設置其角色信息,那么在這里我用班級來表示用戶的角色

  3)用戶的三個重要屬性就是 用戶名,密碼與權限

  4) 這里的返回值(UserDetails)不能返回null,如果根據用戶名找不到對應的用戶可以拋出UsernameNotFoundException異常

2.3 改造WebSecurityConfig

package com.bdqn.lyrk.security.study.app.config;

import com.bdqn.lyrk.security.study.app.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * spring-security的相關配置
 *
 * @author chen.nie
 * @date 2018/6/7
 **/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*
            1.配置靜態資源不進行授權驗證
            2.登錄地址及跳轉過后的成功頁不需要驗證
            3.其余均進行授權驗證
         */
        http.
                authorizeRequests().antMatchers("/static/**").permitAll().
                and().authorizeRequests().antMatchers("/user/**").hasRole("7022").
                and().authorizeRequests().anyRequest().authenticated().
                and().formLogin().loginPage("/login").successForwardUrl("/toIndex").permitAll()
                .and().logout().logoutUrl("/logout").invalidateHttpSession(true).deleteCookies().permitAll()
        ;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //設置自定義userService
        auth.userDetailsService(userService);
    }
}
View Code

在這里主要做以下處理:

 1)針對於/user/**路徑的請求需要設置對應的權限

 2) 做用戶注銷的處理,用戶注銷時需要銷毀session與cookie

 3)配置自定義UserDetailService

 

2.4、改造index.jsp

<%--
  Created by IntelliJ IDEA.
  User: chen.nie
  Date: 2018/6/8
  Time: 上午9:56
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
歡迎:${user.username}
<sec:authorize access="hasRole('7022')">
    加入時間:${user.joinTime}
</sec:authorize>
<form action="/logout" method="post">
    <input type="submit" value="退出" />
    <sec:csrfInput/>
</form>

</body>
</html>

  在這里面我們使用spring對security標簽的支持判斷當前用戶是否有對應的角色,另外我們在處理登出操作時必須為post提交且有對應的token防止csrf


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM