Spring Security 入門學習--數據庫認證和授權


  • 首先是使用的SpringBoot框架

   基礎需要的pom以來如下,基礎的springboot項目的創建就不一一贅述了。

        <!--spring web-->     
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
        <!--jpa 對數據庫操作的框架 類似mybatis-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-jpa</artifactId>
         </dependency>
        <!--頁面引擎 thymeleaf模板-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-thymeleaf</artifactId>
         </dependency>
         <!--mysql數據庫驅動-->
         <dependency>
             <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
             <scope>runtime</scope>
         </dependency> 
  • 加入必須的security依賴
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

數據庫連接配置文件

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/springsecurity?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=user spring.datasource.password=password

#設置運行時打印sql語句 spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update #關閉thymeleaf緩存 spring.thymeleaf.cache=false

#不用數據庫認證時默認的用戶名和密碼
#spring.security.user.name=admin
#spring.security.user.password=1234
  • 實體類***********重要***********

  對於實體類的創建:

  1. 用戶表包含權限表
  2. 用戶表不包含權限表

  筆者使用的是用戶表包含權限表的方式,那么不包含權限表該怎么解決呢?

    創建一個新的實體類,如SecurityUser 繼承User 實現UserDetails 它包含User的內容和UserDetails的內容,對於權限的設置就不一樣了

      重點注意這個方法 getAuthorities ,它來將權限交給security管理 暫時可以用Null值 在UserDetailsService中具體賦值

@Entity
public class User implements Serializable, UserDetails {

    private String username;

    private String password;

    private String role;

    public User(){}

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) { this.username = username; }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    /**
     * 指示用戶的賬戶是否已過期。無法驗證過期的賬戶。
     * 如果用戶的賬戶有效(即未過期),則返回true,如果不在有效就返回false
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * 指示用戶是鎖定還是解鎖。無法對鎖定的用戶進行身份驗證。
     * 如果用戶未被鎖定,則返回true,否則返回false
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * 指示用戶的憑證(密碼)是否已過期。過期的憑證阻止身份驗證
     * 如果用戶的憑證有效(即未過期),則返回true
     * 如果不在有效(即過期),則返回false
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }


    /**
     * 指示用戶是啟用還是禁用。無法對禁用的用戶進行身份驗證
     * 如果啟用了用戶,則返回true,否則返回false
     */
    @Override
    public boolean isEnabled() {
        return true;
    }

    /**
     * 得到用戶的權限,如果權限表和用戶表是分開的,我們需要在重新定義一個實體類實現UserDetails 並且繼承於User類
* 交給security的權限,放在UserDetailService進行處理
*/ @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); //角色必須以ROLE_開頭,如果數據庫沒有,則需要加上 至於角色為什么必須要以ROLE_開頭 筆者沒有進行深究 authorities.add(new SimpleGrantedAuthority("ROLE_"+this.role)); return authorities; } }
  •  此處就先不談security的config配置吧,后面會提到

     我們還沒有用到數據庫中的數據,在此我們需要改造UserServiceImpl類,讓其實現 UserDetailsService 並重寫其中的 loadUserByUsername 方法,這是數據庫認證的必要流程,貼代碼:

@Service
public class UserServiceImpl implements UserService, UserDetailsService {
    @Autowired
    UserDao userDao;

    /**
     *  實現security認證實現的方法
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        logger.error("username:" + username);
       //option這個東西請讀者自行研究,此處可以直接是一個user,optional只是用於判斷空值,避開空指針異常的
        Optional<User> byUsername = userDao.findByUsername(username);
        if (!byUsername.isPresent()) {
            throw new UsernameNotFoundException("用戶名不存在,請先注冊后登錄!");
        }else{
            //權限表和用戶表分開************按照下面方式,如果不是,直接返回帶有權限信息的User對象
            //查詢的權限在此處可以通過數據庫查詢,並且賦值
            //List<GrantedAuthority> authorities=new ArrayList<>();
            //authorities.add(new SimpleGrantedAuthority("ROLE_SPITTER"))
           //新創建一個SecurityUser(自定義實現了UserDetails的類)
           //將authorites放到該對象中,並返回出去
            return byUsername.get();
        }
    }
}
  • 對於security的配置******************重要

  新創建一個配置類繼承 WebSecurityConfigurerAdapter 

  需要注意的幾個方法(重寫的方法):

    configure(HttpSecurity http)   :     htpp請求安全處理

    configure(AuthenticationManagerBuilder auth)    :  自定義數據庫權限認證

    configure(WebSecurity web)    :    WEB安全

   

@Configuration
@EnableWebSecurity         // 開啟注解
@EnableGlobalMethodSecurity(prePostEnabled = true)  // 開啟注解



//自定義了一個springSecurity安全框架的配置類 繼承WebSecurityConfigurerAdapter,重寫其中的方法configure,
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserServiceImpl userService;

 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()  //接觸防止跨域請求
                .authorizeRequests()
                .antMatchers("/toLogin","/templates/**","/logout","/toIndex").permitAll()  //忽略認證的url
                .anyRequest().authenticated()  //其他的url都需要被認證才可以訪問
                .and()
                .formLogin()      //允許自定義表單登錄
                .loginPage("/toLogin")  //這是action地址  不能寫具體頁面  確定自定義登錄頁  自己不需要寫login方法  登錄是由security提供
                .loginProcessingUrl("/login")  //這是html中form中action的值 必須要對應
                .defaultSuccessUrl("/toIndex")  //默認登錄成功會跳轉的controller
               //關於登錄失敗的提示信息,請讀者自行解決
                .failureForwardUrl("/login-error")
                .failureUrl("/login-error");
    }


     //密碼加密,最新的security必須使用密碼加密
    @Bean
    public PasswordEncoder passwordEncoder(){
        //使用BCcypt加密
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //從數據庫讀取的用戶進行身份認證 實現了userDetailsService的類
        auth.userDetailsService(userService)
                .passwordEncoder(passwordEncoder());
    }

    //解除對靜態資源的保護
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**","/templates/**");
    }

}
  • 接下來是Controller和頁面
  • 必須要登錄成功才可以訪問其他的接口,沒有通過認證會一直卡在login
@Controller
public class UserController {
    @Autowired
    UserServiceImpl userService;

    @RequestMapping("/toIndex")
    public String index(){
        return "index";
    }

    @RequestMapping("/toLogin")
    public String login(){
        return "login";
    }
    //定義需要訪問的權限
    @PreAuthorize("hasAnyRole('ROLE_admin')")
    @RequestMapping("/sayHello")
    @ResponseBody
    public String sayHello(){
        return "hello";
    }
    //定義需要訪問的權限
    @PreAuthorize("hasAnyAuthority('ROLE_user')")
    @RequestMapping("/sayYes")
    @ResponseBody
    public String sayYes(){
        return "yes";
    }


}

  html頁面

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <!--layui css-->
    <link rel="stylesheet" th:href="@{js/layui-v2.5.5/layui/css/layui.css}">
    <!--layui js-->
    <script th:src="@{js/layui-v2.5.5/layui/layui.js}"></script>
    <!--jquery-->
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <title>登錄頁面</title>
</head>
<body>
        <!--這里請注意action的值需要與loginProcessingUrl 相對應-->
        <form action="/login" method="post">
            <div class="layui-form-item">
                <label class="layui-form-label">輸入框</label>
                <div class="layui-input-inline">
                    <input type="text" id="username" name="username" required  lay-verify="required" placeholder="請輸入用戶名" autocomplete="off" class="layui-input">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">密碼框</label>
                <div class="layui-input-inline">
                    <input type="password"  name="password" required lay-verify="required" placeholder="請輸入密碼" autocomplete="off" class="layui-input">
                </div>
            </div>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn layui-btn-warm" lay-submit lay-filter="formDemo">登錄</button>
                </div>
            </div>
        </form>
</body>
</html>

第一次寫博客 感覺還是有點亂,請和諧討論,筆者也是看了很多其他的博客慢慢弄出來的,希望大家不要害怕麻煩,慢慢來,引入就不貼了,看了太多 也不知道誰是誰的


免責聲明!

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



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