springSecurity自定义用户认证逻辑

1. 创建项目

使用idea中的spring 初始化工具引入springbootspringsecruity初始化项目

最终pom.xml如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo-spring-secruity</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo-spring-secruity</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.10</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

创建一个测试控制器

package com.example.demospringsecruity.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author john
 * @date 2020/1/6 - 9:47
 */
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello spring secruity";
    }
}

运行项目

访问<http://localhost:8081/hello>,会被跳转到<http://localhost:8081/login>

输入用户名user和刚才运行项目时输出的密码,登录成功,可以获取到刚才预期的响应

2. 自定义登录方式

package com.example.demospringsecruity.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @author john
 * @date 2020/1/6 - 10:07
 */
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 表单登录
//        http.formLogin()
        //  基本登录
        http.httpBasic()
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated();
    }
}

当浏览器发送一个请求后,首先会经过第一个过滤器,UsernamePassowordAuthenticationFilter,这个过滤器中,它会判断你的请求是否带usernamepassword这两个参数,如果带了就进行拦截验证,如果没有就进入到第二个过滤器BasicAuthenticationFilter,这个过滤器会判断请求头中是否含有需要验证的信息,如果没有进入下一个拦截器,最后到ExceptionTranslationFilter拦截器,它会捕获FilterSecurityInterceptor抛出的异常,而FilterSecurityInterceptor会判断这个请求是否校验通过,权限是否通过,以上就是Spring Security框架的一个基本认证流程,FilterSecurityInterceptor是这个框架的最后一个拦截器,所有的请求都必须通过该拦截器的校验。

3. 自定义用户认证逻辑

1. 处理用户信息获取逻辑        UserDetailsService
2. 处理用户校验逻辑	          UserDetails
3. 处理密码加密解密           PasswordEncoder

UserDetailsService

用户信息获取逻辑在springsecruity中是被封装在UserDetailService接口中

package org.springframework.security.core.userdetails;

public interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}

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();//账户是否可用
}

下面开始编写具体代码
MyUserDetailsService处理用户信息获取逻辑

package com.example.demospringsecruity.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;

/**
 * @author john
 * @date 2020/1/6 - 10:32
 */
@Component
@Slf4j
public class MyUserDetailsService implements UserDetailsService {
    //这里可以注入mapper或者repository的dao对象来实现数据校验逻辑操作
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("用户名:" + username);
        //这里密码应该从数据库中取出,暂时先使用加密生成
        String password = passwordEncoder.encode("123456");
        //User类是Spring内置的一个类,实现了UserDetails接口,而这个接口是UserDetailSerice的子接口
        return new User(username,
                password,
                true,        // 账户是否可用
                true,        // 账户是否过期
                true,        // 密码是否过期
                true,        //账户是否被锁定
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin") //授权集合
        );
    }
}

PasswordConfig处理密码加密解密
注入PasswordEncoder

package com.example.demospringsecruity.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author john
 * @date 2020/1/6 - 12:51
 */
//说明这个一个配置类,类似spring中的xml文件
@Configuration
public class PasswordConfig {

    //手动将PasswordEncoder注入到ioc容器中
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

测试在输入用户user密码123456后成功获取数据

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

其实非常简单,我们只要将我们自己的User表继承spring-securityUser类(org.springframework.security.core.userdetails.User),再在MyUserDetailsService里只要返回我们自己的user即可.

posted @ 2020-01-06 13:15  if年少有为  阅读(1497)  评论(0编辑  收藏  举报