spring-boot使用spring-security進行身份認證(1)

說明 springboot 版本 2.0.3
源碼地址:點擊跳轉
java

1、 介紹

  Spring Security 是一個可以爲基於 Spring 的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組能夠在 Spring 應用上下文中配置的 Bean,充分利用了 Spring IoC,DI(控制反轉 Inversion of Control ,DI:Dependency Injection 依賴注入)和 AOP(面向切面編程)功能,爲應用系統提供聲明式的安全訪問控制功能,減小了爲企業系統安全控制編寫大量重複代碼的工做。mysql

2、 環境搭建

  創建 springboot2 項目,加入 security 依賴,mybatis 依賴git

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
   <groupId>org.mybatis.spring.boot</groupId>
   <artifactId>mybatis-spring-boot-starter</artifactId>
   <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
複製代碼

數據庫爲傳統的用戶--角色--權限,權限表記錄了 url 和 method,springboot 配置文件以下:github

mybatis:
 type-aliases-package: com.example.demo.entity
server:
 port: 8081
spring:
 datasource:
 driver-class-name: com.mysql.jdbc.Driver
 url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true
 username: root
 password: 123456
 http:
 encoding:
 charset: utf-8
 enabled: true
複製代碼

springboot 啓動類中加入以下代碼,設置路由匹配規則。spring

@Override
protected void configurePathMatch(PathMatchConfigurer configurer) {
    configurer.setUseSuffixPatternMatch(false) //設置路由是否後綴匹配,譬如/user可以匹配/user.,/user.aa
		.setUseTrailingSlashMatch(false); //設置是否後綴路徑匹配,好比/user可以匹配/user,/user/
}
複製代碼

3、 security 配置

  默認狀況下 security 是無需任何自定義配置就可以使用的,咱們不考慮這種方式,直接講如何個性化登陸過程。sql

一、 創建 security 配置文件,目前配置文件中尚未任何配置。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
複製代碼

二、 個性化登陸,security 中的登陸以下:

登陸過程

  • security 須要一個 user 的實體類實現UserDetails接口,該實體類最後與系統中用戶的實體類分開,代碼以下:
public class SecurityUser implements UserDetails{
	private static final long serialVersionUID = 1L;
	private String password;
	private String name;
	List<GrantedAuthority> authorities;

	public SecurityUser(string name,string password) {
		this.id = id;
		this.password = password;
		this.name = name;
		this.age = age;
	}

	public void setAuthorities(List<GrantedAuthority> authorities) {
		this.authorities = authorities;
	}

	@Override
	public Collection<GrantedAuthority> getAuthorities() {
		return this.authorities;
	}

	@Override //獲取校驗用戶名
	public String getUsername() {
		return String.valueOf(this.id);
	}

    @Override //獲取校驗用密碼
	public String getPassword() {
		return password;
	}

	@Override //帳戶是否未過時
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override  //帳戶是否未鎖定
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override  //賬戶密碼是否未過時,通常有的密碼要求性高的系統會使用到,比較每隔一段時間就要求用戶重置密碼
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override //帳戶是否可用
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		return true;
	}
}
複製代碼
  • 編寫了實體類還須要編寫一個服務類 SecurityService 實現UserDetailsService接口,重寫 loadByUsername 方法,經過這個方法根據用戶名獲取用戶信息,代碼以下:
@Component
public class SecurityUserService implements UserDetailsService {
    @Autowired
    private JurisdictionMapper jurisdictionMapper;
    @Autowired
    private UserMapper userMapper;
    private Logger log = LoggerFactory.getLogger(this.getClass());


    @Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		log.info("登陸用戶id爲:{}",username);
		int id = Integer.valueOf(username);
		User user = userMapper.getById(id);
		if(user==null) {
            //拋出錯誤,用戶不存在
			throw new UsernameNotFoundException("用戶名 "+username+"不存在");
		}
		//獲取用戶權限
		List<GrantedAuthority> authorities = new ArrayList<>();
		List<Jurisdiction> jurisdictions = jurisdictionMapper.selectByUserId(id);
		for(Jurisdiction item : jurisdictions) {
            GrantedAuthority authority = new MyGrantedAuthority(item.getMethod(),item.getUrl());
			authorities.add(authority);
		}
        SecurityUser securityUser = new SecurityUser(user.getName(),user.getPassword(),authority):
		user.setAuthorities(authorities);
		return securityUser;
	}
}
複製代碼
  • 一般咱們會對密碼進行加密,全部還要編寫一個 passwordencode 類,實現 PasswordEncoder 接口,代碼以下:
@Component
public class MyPasswordEncoder implements PasswordEncoder {
	private Logger log = LoggerFactory.getLogger(this.getClass());

	@Override //不清楚除了在下面方法用到還有什麼用處
	public String encode(CharSequence rawPassword) {
		return StringUtil.StringToMD5(rawPassword.toString());
	}

	//判斷密碼是否匹配
	@Override
	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		return encodedPassword.equals(this.encode(rawPassword));
	}
}
複製代碼

三、 編輯配置文件

  • 編寫 config Bean 以使用上面定義的驗證邏輯,securityUserService、myPasswordEncoder 經過@Autowired 引入。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(securityUserService)
        .passwordEncoder(myPasswordEncoder);
}
複製代碼
  • 而後編寫 configure Bean(和上一個不同,參數不一樣),實現 security 驗證邏輯,代碼以下:
@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
		.csrf() //跨站
		.disable() //關閉跨站檢測
		.authorizeRequests()//驗證策略策略鏈
			.antMatchers("/public/**").permitAll()//無需驗證路徑
           .antMatchers("/login").permitAll()//放行登陸
			.antMatchers(HttpMethod.GET, "/user").hasAuthority("getAllUser")//擁有權限纔可訪問
			.antMatchers(HttpMethod.GET, "/user").hasAnyAuthority("1","2")//擁有任一權限便可訪問
			//角色相似,hasRole(),hasAnyRole()
			.anyRequest().authenticated()
		.and()
		.formLogin()
			.loginPage("/public/unlogin") //未登陸跳轉頁面,設置了authenticationentrypoint後無需設置未登陸跳轉頁面
			.loginProcessingUrl("/public/login")//處理登陸post請求接口,無需本身實現
            .successForwardUrl("/success")//登陸成功轉發接口
            .failureForwardUrl("/failed")//登陸失敗轉發接口
            .usernameParameter("id") //修改用戶名的表單name,默認爲username
            .passwordParameter("password")//修改密碼的表單name,默認爲password
		.and()
		.logout()//自定義登出
			.logoutUrl("/public/logout") //自定義登出api,無需本身實現
            .logoutSuccessUrl("public/logoutSuccess")
	}
複製代碼

到這裏即可實現 security 與 springboot 的基本整合。數據庫

4、實現記住我功能

一、 建表

  記住我功能須要數據庫配合實現,首先要在數據庫建一張表用戶保存 cookie 和用戶名,數據庫建表語句以下:不能作修改編程

CREATE TABLE `persistent_logins` (
  `username` varchar(64) NOT NULL,
  `series` varchar(64) NOT NULL,
  `token` varchar(64) NOT NULL,
  `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`series`)
)
複製代碼

二、 編寫 rememberMeservice Bean

  代碼以下:api

@Bean
    public RememberMeServices rememberMeServices(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        PersistentTokenBasedRememberMeServices rememberMeServices =
                new PersistentTokenBasedRememberMeServices("INTERNAL_SECRET_KEY",securityUserService,jdbcTokenRepository);
        //還可設置許多其餘屬性
       rememberMeServices.setCookieName("kkkkk"); //客戶端cookie名
        return rememberMeServices;
    }
複製代碼

dataSource 爲@Autowired 引入安全

三、 配置文件設置 remember

  在 config(HttpSecurity http)中加入記住我功能

.rememberMe()
    .rememberMeServices(rememberMeServices())
    .key("INTERNAL_SECRET_KEY")
複製代碼

在登陸表單中設置 remember-me 便可實現記住我功能。

本文原創發佈於:www.tapme.top/blog/detail…

相關文章
相關標籤/搜索