天天看點

學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼

Spring Security

思維導圖

學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼

簡介

SpringSecurity是Spring下的一個安全架構,與shiro 類似,一般用于使用者認證(Authentication)和使用者授權(Authorization)兩個部分,常與與SpringBoot相整合。

初階 Spring Security

添加maven依賴

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

重新部署,會看到一個登陸頁面。

學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼

密碼,翻看日志可以檢視到

學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼

使用者名預設為 user

輸入使用者名和密碼

學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼
學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼

登入成功

中階 Security 記憶體使用者名和密碼

使用者名密碼登入

在上面的章節中,添加了Security依賴,直接就出現了登入界面,這次讓使用者名,密碼儲存到記憶體當中

寫一個extends WebSecurityConfigurerAdapter的配置類:

配置類内容如下

package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {
    //密碼加密函數
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    //password為BCryptPasswordEncoder加密123後的值
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("admin").password("$2a$10$2cPRItUHyE1GSZnrYWHiQevpbxn4ikWgOa1PYL5miWvqK8GFVCWb6").roles("admin")
                .and().withUser("java").password("$2a$10$rygGQylvmoAFmPcKQP6xvepNVAw9Bxp0sbAphxKQwhAV79Au0ECvq").roles("user");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasAnyRole("admin","user")
                .anyRequest().authenticated()  //其他路徑認證之後就可以通路
                .and()
                .formLogin()
                .loginProcessingUrl("/doLogin")  //處理登入請求的位址
                .permitAll()
                .and()
                .csrf().disable();
    }
}

           

輸入使用者名,密碼,進行登入

學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼

可以看到,登入成功

學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼

其中,

1.通過 @EnableWebSecurity注解開啟Spring Security的功能。使用@EnableGlobalMethodSecurity(prePostEnabled = true)這個注解,可以開啟security的注解,我們可以在需要控制權限的方法上面使用@PreAuthorize,@PreFilter這些注解。

2.extends 繼承 WebSecurityConfigurerAdapter 類,并重寫它的方法來設定一些web安全的細節。我們結合@EnableWebSecurity注解和繼承WebSecurityConfigurerAdapter,來給我們的系統加上基于web的安全機制。

## 角色權限控制

當我們的系統功能子產品當需求發展到一定程度時,會不同的使用者,不同角色使用我們的系統。這樣就要求我們的系統可以做到,能夠對不同的系統功能子產品,開放給對應的擁有其通路權限的使用者使用。

Spring Security提供了Spring EL表達式,允許我們在定義URL路徑通路(@RequestMapping)的方法上面添加注解,來控制通路權限。

在标注通路權限時,根據對應的表達式傳回結果,控制通路權限:

true,表示有權限

fasle,表示無權限

建立新的控制器

學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼
package com.example.demo.web;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class Test {
    @RequestMapping("/mapping")
    @ResponseBody
    public String test(){
        return "test";
    }
}
           

可以看到已經通路成功

學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼

添權重限注解

package com.example.demo.web;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class Test {
    @RequestMapping("/mapping")
    @ResponseBody
    @PreAuthorize("hasRole('admin')") // Spring Security預設的角色字首是”ROLE_”,使用hasRole方法時已經預設加上了
    public String test(){
        return "test";
    }
}
           

此時通路

學習學習SpringSecurity簡介初階 Spring Security中階 Security 記憶體使用者名和密碼高階: 使用資料庫儲存使用者名和密碼

403錯誤

高階: 使用資料庫儲存使用者名和密碼

使用者表

@Entity
class User {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @BeanProperty
  var id: Integer = _
  @BeanProperty
  var userName: String = _
  @BeanProperty
  var password: String = _

}
           

角色表

···

@Entity

class Role {

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

@BeanProperty

var id: Integer = _

var role: String = _

}

使用者角色表

@Entity
class UserRole {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @BeanProperty
  var id: Integer = _
  @BeanProperty
  var userId: Integer = _
  @BeanProperty
  var roleId: Integer = _


}
           

更改SpringSecurity

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.inMemoryAuthentication().withUser("admin").password("$2a$10$2cPRItUHyE1GSZnrYWHiQevpbxn4ikWgOa1PYL5miWvqK8GFVCWb6").roles("admin")
        .and().withUser("java").password("$2a$10$rygGQylvmoAFmPcKQP6xvepNVAw9Bxp0sbAphxKQwhAV79Au0ECvq").roles("user");           

更改為

@Override
    @Bean
    public UserDetailsService userDetailsService() { //覆寫寫userDetailsService方法 (1)
        return new LightSwordUserDetailService();

    }



//AuthenticationManager使用我們的 lightSwordUserDetailService 來擷取使用者資訊
        auth.userDetailsService(userDetailsService());
           

具體的 new LightSwordUserDetailService(); 實作類,需要自己進行配置

這個接口需要我們實作一個方法:loadUserByUsername。即從資料庫中取出使用者名、密碼以及權限相關的資訊。最後傳回一個UserDetails 實作類。

@Service
class LightSwordUserDetailService extends UserDetailsService {

  @Autowired var userRoleDao: UserRoleDao = _
  @Autowired var userDao: UserDao = _
  @Autowired var roleDao: RoleDao = _


  override def loadUserByUsername(username: String): UserDetails = {

//    val user = userDao.findByUsername(username) // 直接調用jpa自動生成的方法
    val user = userDao.getUserByUsername(username)
    if (user == null) throw new UsernameNotFoundException(username + " not found")

    val authorities = new util.ArrayList[SimpleGrantedAuthority]
    val userRoles = userRoleDao.listByUserId(user.id)

    // Scala中調用java的collection類,使用scala的foreach,編譯器會提示無法找到result的foreach方法。因為這裡的userRoles的類型為java.util.List。若要将其轉換為Scala的集合,就需要增加如下語句:
    import scala.collection.JavaConversions._
    for (userRole <- userRoles) {
      val roleId = userRole.roleId
      val roleName = roleDao.findOne(roleId).role
      if (!StringUtils.isEmpty(roleName)) {
        authorities.add(new SimpleGrantedAuthority(roleName))
      }

      System.err.println("username is " + username + ", " + roleName)
    }

    new User(username, user.password, authorities)
  }
}
           

在上方,通過内部類的方式,擷取到了一個User對象。并添加進入了UserDetails中

這樣就完成了高階教程