spring-security入门2---自定义用户认证逻辑


项目源码地址 https://github.com/nieandsun/security

1. 如何使用自己的认证逻辑

  通过上篇文章已经对spring-security有了初步的认识,但是现实项目中用户名不可能只为user,密码也不可能是随着项目的启动来自动生成.
  那如何不使用spring-security的这种默认机制呢?其实很简单,只需要将一个实现了UserDetailsService接口的类注入到spring容器中,当我们访问该springboot项目时,springboot会自动查看spring容器中是否有实现了该接口的类,如果有的话,该springboot项目就会按照该类中的认证逻辑进行校验,而不再使用上篇文章中介绍的那种默认机制了.

2. UserDetailsService简介及自定义用户认证逻辑代码开发

2.1 UserDetailsService相关内容简单介绍

UserDetailsService的源码如下:

package org.springframework.security.core.userdetails;
public interface UserDetailsService {
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

UserDetailsService为一个接口,里面只有一个抽象方法loadUserByUsername,其返回值为UserDetails,UserDetails的源码如下,也是一个接口:

public interface UserDetails extends Serializable {
	Collection<? extends GrantedAuthority> getAuthorities();//权限的集合
	String getPassword();//密码
	String getUsername();//用户名
	boolean isAccountNonExpired();//账户是否没过期 true-->没过期
	boolean isAccountNonLocked();//账户是否没锁定 true-->没被锁定
	boolean isCredentialsNonExpired();//凭证是否没过期 true-->没过期
	boolean isEnabled();//账户是否可用
}

2.2 用户认证逻辑代码开发

编写实现UserDetailsService接口的类,具体代码和解释如下:
在这里插入图片描述
再贴一下上面的代码吧:

package com.nrsc.security.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //点击页面的登陆时,你实际需要做的事为
        //1.拿着用户名去数据库里查询其密码
        //2.将拿到的用户名和密码封装到User里进行返回

        log.info("用户名为:" + username);
        //假设下面的密码是根据用户名获得的
        String password = passwordEncoder.encode("123456");
        log.error("password:" + password);
        return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

以及注入PasswordEncoder实现类到spring容器的代码,这里写在了SecurityConfig里

package com.nrsc.security.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                // .and()
                // .httpBasic()
                .and()
                .formLogin();
    }
}

3. 测试

3.1 启动项目,访问http://localhost:8080/hello

在这里插入图片描述

3.2 输入错误密码,如"123",会显示用户名或密码错误

在这里插入图片描述

3.3 输入正确密码"123456",会访问到想要访问的资源

在这里插入图片描述

4. 关于UserDetailsService和其实现类User的说明

2中的代码

return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));

从中很明显可以知道User为UserDetailsService的一个实现类,但是在2.1中可以看到UserDetailsService其实有7个属性,而我们返回的User只有三个属性,那么很容易就可以猜测到User给了另外四个参数默认值,查看源码我们就可以得到证实:

	/**
	 * Calls the more complex constructor with all boolean arguments set to {@code true}.
	 */
	public User(String username, String password,
			Collection<? extends GrantedAuthority> authorities) {
		this(username, password, true, true, true, true, authorities);
	}

其实User还有一个7参数的构造函数,源码如下:

	public User(String username, String password, boolean enabled,
			boolean accountNonExpired, boolean credentialsNonExpired,
			boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {

		if (((username == null) || "".equals(username)) || (password == null)) {
			throw new IllegalArgumentException(
					"Cannot pass null or empty values to constructor");
		}

		this.username = username;
		this.password = password;
		this.enabled = enabled;
		this.accountNonExpired = accountNonExpired;
		this.credentialsNonExpired = credentialsNonExpired;
		this.accountNonLocked = accountNonLocked;
		this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
	}

如果我们的业务用到这些参数时,可以使用该构造函数.这四个值中只要有一个参数为false,即使密码输入对了认证也无法通过.比如说2中的代码我们按照如下方式返回User时:

 return new User(username, password, true,true,true,false,
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));

我们访问http://localhost:8080/hello进入登陆页面,输入用户名和密码时,会报如下错误:
在这里插入图片描述

5. 如何使用自己定义的user类???

上面的演示我使用了spring-security自己的UserDetails实现类User(org.springframework.security.core.userdetails.User),但实际开发中我们不可能这么用,因为根据实际的业务,往往会希望用户信息表里会包含更多的数据,比如说电话,邮箱,公司职位等信息,那我们该如何去处理呢?

其实非常简单,我们只要将我们自己的User表继承spring-security的User类(org.springframework.security.core.userdetails.User)就行了

比如:

package com.nrsc.security.domain;


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

import java.util.Collection;

/**
 * 该类就是实际生产中我们自己的user类了,它对应于我们数据库中的用户信息表
 */
@Data
public class NrscUser extends User {


    /**
     * 实现父类的构造方法
     * @param username
     * @param password
     * @param authorities
     */
    public NrscUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }

    /**
     * 实现父类构造方法
     * @param username
     * @param password
     * @param enabled
     * @param accountNonExpired
     * @param credentialsNonExpired
     * @param accountNonLocked
     * @param authorities
     */
    public NrscUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
    }

    /**
     * 电话
     */
    private String mobile;

    /**
     * email
     */
    private String email;

}

此时MyUserDetailsService里只要返回我们自己的user就行了,比如

return new NrscUser(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值