你是谁:who are you?
专业一点叫: Authentication(认证):
a. 内存认证
b. jdbc认证
c. UserDetailsService认证
d. ldap 认证
下面是源码中的四个认证方法
你能干什么:what are you allwoed to do?
pom.xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>wx0725.topgroupId>
<artifactId>guojihuaartifactId>
<version>1.0.0version>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.3.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>2.1.3.RELEASEversion>
plugin>
plugins>
build>
project>
写一个配置类,自定义内存认证。
package wx0725.top.config;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
/**
* @author WEN
* @version 1.0
* @description: Wen Xuan
* @date 2021/5/7 下午 19:42
* @link http://wx0725.top
*/
@EnableWebSecurity
// 上面一个注解中包含下面三个注解
//@Import
//@EnableGlobalAuthentication
//@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
// 选择定义密码加密算法
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// 内存认证使用这个密码加密
authenticationManagerBuilder.inMemoryAuthentication().passwordEncoder(bCryptPasswordEncoder)
// 注册一个身份,并且密码需要加密
.withUser("文轩").password(bCryptPasswordEncoder.encode("0725")).roles("common")
.and()
.withUser("wenxuan").password(bCryptPasswordEncoder.encode("wenxuan")).roles("vip");
}
}
新建一个index.html
<html>
<head>
<title>首页title>
head>
<body>
<h1>hello world!h1>
body>
html>
运行项目
输入:127.0.0.1:8082 也就是直接访问 spring boot 项目地址
此时会自动跳珠到login
可以看到login页面是从127.0.0.1:8082重定向过去的
需要新建一个安全检测的表格:
在内存认证的配置类中如下代码:
package wx0725.top.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
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 javax.sql.DataSource;
/**
* @author WEN
* @version 1.0
* @description: Wen Xuan
* @date 2021/5/7 下午 19:42
* @link http://wx0725.top
*/
@EnableWebSecurity
// 上面一个注解中包含下面三个注解
//@Import
//@EnableGlobalAuthentication
//@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
// 选择定义密码加密算法
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// 内存认证使用这个密码加密
authenticationManagerBuilder.inMemoryAuthentication().passwordEncoder(bCryptPasswordEncoder)
// 注册一个身份,并且密码需要加密
.withUser("user").password(bCryptPasswordEncoder.encode("user")).roles("common")
.and()
.withUser("admin").password(bCryptPasswordEncoder.encode("admin")).roles("common");
// 使用JDBC认证
// 查询客户
String usql = "select username,password,valid from t_customer where username=?";
// 查询客户对应的身份
String asql = "select c.username, a.authority from t_customer c,t_authority a,t_customer_authority ca where ca.customer_id=c.id and ca.authority_id=a.id and c.username=?";
authenticationManagerBuilder.jdbcAuthentication().passwordEncoder(bCryptPasswordEncoder)
.dataSource(dataSource)
.usersByUsernameQuery(usql)
.authoritiesByUsernameQuery(asql);
}
// 允许忽略静态资源的安全访问
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/fonts/**");
}
}
登录成功之后会跳转到首页
这里其实跳到首页是因为第一次输入的时候,输入的是127.0.0.1:8082
如果你输入的是其他的页面,在登录成功之后会跳转到那个页面,并非跳转到首页。
这一部分代码不少:
其主要实现的是减少数据库压力,通过用户缓存信息,实现验证登录,在登录的时候只查询一次数据库,将其保存在缓存中,下次查询就直接读取缓存。
package wx0725.top.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;
/**
* @author WEN
* @version 1.0
* @description: Wen Xuan
* @date 2021/5/8 下午 13:58
* @link http://wx0725.top
*
* 用户信息
*/
@Entity(name = "t_customer")
public class Customer implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
private Integer valid;
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", valid=" + valid +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer 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 Integer getValid() {
return valid;
}
public void setValid(Integer valid) {
this.valid = valid;
}
}
package wx0725.top.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;
/**
* @author WEN
* @version 1.0
* @description: Wen Xuan
* @date 2021/5/8 下午 14:13
* @link http://wx0725.top
*
* 用户权限
*/
@Entity(name = "t_authority")
public class Authority implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String authority;
@Override
public String toString() {
return "Authority{" +
"id=" + id +
", authority='" + authority + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
}
package wx0725.top.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import wx0725.top.domain.Customer;
/**
* @author WEN
* @version 1.0
* @description: Wen Xuan
* @date 2021/5/8 下午 13:56
* @link http://wx0725.top
*
* 用户信息查询接口
*/
public interface CustomerRepository extends JpaRepository<Customer, Integer> {
// 可以直接使用JPA生成的实现
// @Query(value = "select c.* from t_customer c where c.username=?1", nativeQuery = true)
Customer findByUsername(String username);
}
package wx0725.top.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import wx0725.top.domain.Authority;
import java.util.List;
/**
* @author WEN
* @version 1.0
* @description: Wen Xuan
* @date 2021/5/8 下午 13:57
* @link http://wx0725.top
*/
public interface AuthorityRepository extends JpaRepository<Authority, Integer> {
// a.*
@Query(value = "select a.* from t_customer c,t_authority a,t_customer_authority ca where ca.customer_id=c.id and ca.authority_id=a.id and c.username=?1", nativeQuery = true)
List<Authority> findAuthoritiesByUsername(String s);
}
package wx0725.top.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import wx0725.top.domain.Authority;
import wx0725.top.domain.Customer;
import wx0725.top.repository.AuthorityRepository;
import wx0725.top.repository.CustomerRepository;
import java.util.List;
/**
* @author WEN
* @version 1.0
* @description: Wen Xuan
* @date 2021/5/8 下午 13:54
* @link http://wx0725.top
*
* 业务处理
* 用来通过用户名查询用户信息、权限
*/
@Service
public class CustomerService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private AuthorityRepository authorityRepository;
@Autowired
private RedisTemplate redisTemplate;
private String cacheName = "customer::";
// 用户名查询用户
public Customer getCustomer(String username) {
Customer customer = null;
Object o = redisTemplate.opsForValue().get(cacheName + "customer_" + username);
if (o != null) {
customer = (Customer) o;
} else {
customer = customerRepository.findByUsername(username);
if (customer != null) {
redisTemplate.opsForValue().set(cacheName + "customer_" + username, customer);
}
}
return customer;
}
public List<Authority> getCustomerAuthority(String s) {
List<Authority> authorityList = null;
Object o = redisTemplate.opsForValue().get(cacheName + "authorities_" + s);
if (o != null) {
authorityList = (List<Authority>) o;
} else {
authorityList = authorityRepository.findAuthoritiesByUsername(s);
if (authorityList.size() > 0) {
redisTemplate.opsForValue().set(cacheName + "authorities_" + s, authorityList);
}
}
return authorityList;
}
}
package wx0725.top.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.stereotype.Service;
import wx0725.top.domain.Authority;
import wx0725.top.domain.Customer;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author WEN
* @version 1.0
* @description: Wen Xuan
* @date 2021/5/8 下午 14:11
* @link http://wx0725.top
*
* 查询信息权限封装
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private CustomerService customerService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
// 获取用户信息
Customer customer = customerService.getCustomer(s);
// 获取权限
List<Authority> authorityList = customerService.getCustomerAuthority(s);
// 信息权限封装
List<SimpleGrantedAuthority> simpleGrantedAuthorityList = authorityList.stream()
.map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))
.collect(Collectors.toList());
if (customer != null) {
UserDetails userDetails = new User(customer.getUsername(), customer.getPassword(), simpleGrantedAuthorityList);
return userDetails;
} else {
throw new UsernameNotFoundException("用户不存在");
}
}
}
package wx0725.top.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
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 wx0725.top.service.UserDetailsServiceImpl;
import javax.sql.DataSource;
/**
* @author WEN
* @version 1.0
* @description: Wen Xuan
* @date 2021/5/7 下午 19:42
* @link http://wx0725.top
*/
@EnableWebSecurity
// 上面一个注解中包含下面三个注解
//@Import
//@EnableGlobalAuthentication
//@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
public UserDetailsServiceImpl userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
// 选择定义密码加密算法
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// 内存认证使用这个密码加密
authenticationManagerBuilder.inMemoryAuthentication().passwordEncoder(bCryptPasswordEncoder)
// 注册一个身份,并且密码需要加密
.withUser("user").password(bCryptPasswordEncoder.encode("user")).roles("common")
.and()
.withUser("admin").password(bCryptPasswordEncoder.encode("admin")).roles("common");
// 使用JDBC认证
// 查询客户
String usql = "select username,password,valid from t_customer where username=?";
// 查询客户对应的身份
String asql = "select c.username, a.authority from t_customer c,t_authority a,t_customer_authority ca where ca.customer_id=c.id and ca.authority_id=a.id and c.username=?";
// authenticationManagerBuilder.jdbcAuthentication().passwordEncoder(bCryptPasswordEncoder)
// .dataSource(dataSource)
// .usersByUsernameQuery(usql)
// .authoritiesByUsernameQuery(asql);
// UserDetailsServiceImpl
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
}
下面是缓存