步骤:
- 创建表 admins。
- 新建一个实体类AdminEntity,实现接口:org.springframework.security.core.userdetails.UserDetails(包含自定义的字段)。
- 创建对应于表 admins 的Mapper接口:AdminMapper。
- 创建AdminService接口并实现接口:org.springframework.security.core.userdetails.UserDetailsService。
- 创建类AdminServiceImpl并实现AdminService接口。主要是实现方法:loadUserByUsername
- 创建配置类,实现接口:org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
- 创建配置类,实现接口:org.springframework.security.authentication.AuthenticationProvider(登录验证逻辑)
- 获取登录帐号的信息
开始:
1.建表:
CREATE TABLE `grp_admins` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`insert_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` int(4) NOT NULL DEFAULT '0' COMMENT '账号是否注销',
`user_name` varchar(50) NOT NULL COMMENT '用户账号',
`pwd` varchar(255) NOT NULL COMMENT '密码',
`memo` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `user_name` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='管理员账户表';
2.创建实体类:实现接口org.springframework.security.core.userdetails.UserDetails
package com.xxx.entity;
import java.sql.Timestamp;
import java.util.Collection;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@Data
public class AdminEntity implements UserDetails{
private Long id;
private Timestamp insertTime;
private Timestamp updateTime;
private Integer isDeleted;
private String userName;
private String pwd;
private String memo;
@Override
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return pwd;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return true;
}
}
3.创建AdminMapper:
package com.xxx.mapper;
import com.wansecheng.entity.AdminEntity;
import java.util.List;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
@Mapper
public interface AdminMapper {
@Select("SELECT * FROM grp_admins WHERE id=#{id}")
public AdminEntity getById(Long id);
@Select("SELECT * FROM grp_admins WHERE is_deleted=0 AND user_name=#{username}")
public AdminEntity getByUserName(String username);
@Select("<script>SELECT * FROM grp_admins WHERE is_deleted=0<if test=\"username!=null and username!=''\"> AND user_name LIKE '%${username}%'</if></script>")
public List<AdminEntity> search(String username);
@Options(useGeneratedKeys = true, keyColumn = "id")
@Insert("INSERT INTO grp_admins (user_name, pwd, memo) VALUES (#{userName}, #{pwd}, #{memo})")
public Integer insert(AdminEntity admin);
@Update("UPDATE grp_admins SET memo=#{memo} WHERE id=#{id}")
public Integer update(AdminEntity admin);
@Update("UPDATE grp_admins SET is_deleted=1 WHERE id=#{id}")
public Integer deleteById(Long id);
}
4.创建Service接口:AdminService
package com.xxx.service;
import com.wansecheng.entity.AdminEntity;
import java.util.List;
import org.springframework.security.core.userdetails.UserDetailsService;
public interface AdminService extends UserDetailsService{
public AdminEntity getById(Long id);
public AdminEntity getLoginAdminByUsername(String username);
public List<AdminEntity> search(String username);
public Long save(AdminEntity admin);
public Integer removeById(Long id);
}
5.创建类AdminServiceImpl实现AdminService接口(主要是实现与登录验证有关的方法loadUserByUsername)
package com.xxx.service.impl;
import com.wansecheng.entity.AdminEntity;
import com.wansecheng.mapper.AdminMapper;
import com.wansecheng.service.AdminService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class AdminServiceImpl implements AdminService {
@Autowired
private AdminMapper adminMapper;
@Override
public AdminEntity getById(Long id) {
return adminMapper.getById(id);
}
@Override
public AdminEntity getLoginAdminByUsername(String username) {
return adminMapper.getByUserName(username);
}
@Override
public List<AdminEntity> search(String username) {
return adminMapper.search(username);
}
@Override
public Long save(AdminEntity admin) {
if(admin.getId() == null){
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
admin.setPwd(passwordEncoder.encode(admin.getPwd()));
adminMapper.insert(admin);
}else{
adminMapper.update(admin);
}
return admin.getId();
}
@Override
public Integer removeById(Long id) {
return adminMapper.deleteById(id);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return adminMapper.getByUserName(username);
}
}
6.创建配置类,实现接口:org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
package com.xxx.config;
import com.wansecheng.service.impl.AdminServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
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;
@EnableWebSecurity
public class AdminWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Autowired
AdminServiceImpl adminServiceImpl;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**").authenticated() // 限制/admin下的页面必须登录后才可访问
.antMatchers("/**").permitAll() // 非 /admin下的页面可以访问
.and()
.formLogin() // 表示使用自定义登录表单
.loginPage("/admin/login") // 自定义登录页面(使用controller),页面上的表单要post到/admin/loginPage
.usernameParameter("uname").passwordParameter("pwd") // 自定义登录表单输入框名称
.defaultSuccessUrl("/admin/dashboard") // 登录成功后的默认页
.permitAll() // 登录页允许没登录的情况访问
.and()
.rememberMe() // 表示在登录表单中使用记住我功能
.rememberMeParameter("remember") // 表单记住我的checkbox元素的name
.userDetailsService(adminServiceImpl) // 记住我功能要用到,需要用到UserDetailsService
.and()
.logout() // 表示使用自定义登出表单
.logoutUrl("/admin/logout") // 自定义登出页面(使用controller),页面上的表单要POST到/admin/logoutPage
.logoutSuccessUrl("/admin/login?logout") // 登出后跳转到的页面
.permitAll() // 登出页允许没登录的情况访问
.and().headers().frameOptions().disable() // 允许iframe中发送的请求
.and().csrf().disable(); // 先禁用掉csrf,否则POST的时候会报403
super.configure(http); //To change body of generated methods, choose Tools | Templates.
}
}
7.创建配置类,实现接口:org.springframework.security.authentication.AuthenticationProvider
package com.xxx.config;
import com.wansecheng.service.impl.AdminServiceImpl;
import java.util.ArrayList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Component;
@Component
public class AdminAuthenticationProvider implements AuthenticationProvider {
@Autowired
private AdminServiceImpl adminServiceImpl;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName(); // 获取提交的帐号
String password = authentication.getCredentials().toString(); // 获取提交的密码
/*
String usernameDb = "uss"; // 帐号(模拟从数据库中取出来)
String passwordDb = "$2a$10$nt24iQDnTy0OFgM3i5cS2ufghcP1/T/ygn8NWxfLzMzkFn9bsMoVW"; // 密码是:abc
boolean isPassword = BCrypt.checkpw(password, passwordDb); // 比较密码是否一致
*/
UserDetails admin = adminServiceImpl.loadUserByUsername(username);
//boolean isPassword = password.equals(admin.getPwd());
boolean isPassword = BCrypt.checkpw(password, admin.getPassword());
if (admin != null && isPassword) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password, new ArrayList()); // 创建认证的token
token.setDetails(admin); // 将登录帐号的信息放入token,以便Controller可以获取相关信息
// 登录成功返回
return token;
} else {
// 帐号或密码校验失败
throw new BadCredentialsException("username or password invalid.");
}
}
@Override
public boolean supports(Class<?> type) {
return true;
}
}
8.获取登录帐号的信息:
package com.xxx.controller;
import com.wansecheng.entity.AdminEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
public class IndexController {
@GetMapping("/test")
public String index(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
AdminEntity admin = (AdminEntity)authentication.getDetails();
return admin.getId().toString(); // 这里获取到了登录帐号的ID
}
}