用户需要有什么样的信息。
-- ----------------------------
-- 1、用户信息表
-- ----------------------------
drop table if exists sys_user;
create table if not exists sys_user
(
user_id int8,
user_name varchar(30) not null,
nick_name varchar(30) not null,
user_type varchar(10) default 'sys_user'::varchar,
email varchar(50) default ''::varchar,
phonenumber varchar(11) default ''::varchar,
sex char default '0'::bpchar,
avatar int8,
password varchar(100) default ''::varchar,
status char default '0'::bpchar,
del_flag char default '0'::bpchar,
login_ip varchar(128) default ''::varchar,
login_date timestamp,
create_by int8,
create_time timestamp,
update_by int8,
update_time timestamp,
remark varchar(500) default null::varchar,
constraint "sys_user_pk" primary key (user_id)
);
comment on table sys_user is '用户信息表';
comment on column sys_user.user_id is '用户ID';
comment on column sys_user.user_name is '用户账号';
comment on column sys_user.nick_name is '用户昵称';
comment on column sys_user.user_type is '用户类型(sys_user系统用户)';
comment on column sys_user.email is '用户邮箱';
comment on column sys_user.phonenumber is '手机号码';
comment on column sys_user.sex is '用户性别(0男 1女 2未知)';
comment on column sys_user.avatar is '头像地址';
comment on column sys_user.password is '密码';
comment on column sys_user.status is '帐号状态(0正常 1停用)';
comment on column sys_user.del_flag is '删除标志(0代表存在 2代表删除)';
comment on column sys_user.login_ip is '最后登陆IP';
comment on column sys_user.login_date is '最后登陆时间';
comment on column sys_user.create_by is '创建者';
comment on column sys_user.create_time is '创建时间';
comment on column sys_user.update_by is '更新者';
comment on column sys_user.update_time is '更新时间';
comment on column sys_user.remark is '备注';
-- ----------------------------
-- 初始化-用户信息表数据
-- ----------------------------
insert into sys_user values(1, 'admin', 'eminem', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 1, now(), null, null, '管理员');
insert into sys_user values(2, 'xie', '卡特琳娜', 'sys_user', 'crazyLionLi@qq.com', '15666666666', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 1, now(), null, null, '测试员');
其中constraint “sys_user_pk” primary key (user_id):
创建表时,将 “sys_user_pk” 约束设置为该表的主键,主键的列为 user_id。这意味着在表中,每个唯一的 user_id 值都将唯一标识一行,并且不能重复。通过将 “sys_user_pk” 约束设置为主键,可以确保在执行插入、更新或删除操作时,数据库系统将确保 user_id 的唯一性。
创建系统授权表-规定登录类型,登录id
-- ----------------------------
-- 系统授权表
-- ----------------------------
drop table if exists sys_client;
create table sys_client (
id int8,
client_id varchar(64) default ''::varchar,
client_key varchar(32) default ''::varchar,
client_secret varchar(255) default ''::varchar,
grant_type varchar(255) default ''::varchar,
device_type varchar(32) default ''::varchar,
active_timeout int4 default 1800,
timeout int4 default 604800,
status char(1) default '0'::bpchar,
del_flag char(1) default '0'::bpchar,
create_by int8,
create_time timestamp,
update_by int8,
update_time timestamp,
constraint sys_client_pk primary key (id)
);
comment on table sys_client is '系统授权表';
comment on column sys_client.id is '主建';
comment on column sys_client.client_id is '客户端id';
comment on column sys_client.client_key is '客户端key';
comment on column sys_client.client_secret is '客户端秘钥';
comment on column sys_client.grant_type is '授权类型';
comment on column sys_client.device_type is '设备类型';
comment on column sys_client.active_timeout is 'token活跃超时时间';
comment on column sys_client.timeout is 'token固定超时';
comment on column sys_client.status is '状态(0正常 1停用)';
comment on column sys_client.del_flag is '删除标志(0代表存在 2代表删除)';
comment on column sys_client.create_by is '创建者';
comment on column sys_client.create_time is '创建时间';
comment on column sys_client.update_by is '更新者';
comment on column sys_client.update_time is '更新时间';
insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 1, now(), 1, now());
insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'app', 1800, 604800, 0, 0, 1, now(), 1, now());
登录
package com.xie.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.ObjectUtil;
import com.xie.common.core.domain.R;
import com.xie.common.core.domain.model.LoginBody;
import com.xie.common.core.utils.StringUtils;
import com.xie.system.domain.SysClient;
import com.xie.system.service.impl.SysClientService;
import com.xie.web.domain.LoginVo;
import com.xie.web.service.IAuthStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @作者:xie
* @时间:2023/7/6 15:38
*/
@Slf4j
@SaIgnore
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/auth")
public class AuthController {
private final SysClientService sysClientService;
@PostMapping("/login")
public R<LoginVo> Login(@Validated @RequestBody LoginBody loginBody){
String clientId = loginBody.getClientId();
String grantType = loginBody.getGrantType();
SysClient sysClient = sysClientService.queryByClientId(clientId);
if(ObjectUtil.isNull(sysClient) || !StringUtils.contains(sysClient.getGrantType(),grantType)){
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
return R.fail("auth.grant.type.error");
}
// 登录
return R.ok(IAuthStrategy.login(loginBody, sysClient));
}
}
判断客户端被授权的pc,app是否一致
/**
* 查询客户端管理
*/
@Override
public SysClient queryByClientId(String clientId) {
return sysClientMapper.selectOne(
new LambdaQueryWrapper<SysClient>()
.eq(SysClient::getClientId, clientId)
);
}
面向接口-配置登录策略 - password登录
package com.xie.web.service;
import com.xie.common.core.domain.model.LoginBody;
import com.xie.common.core.exception.ServiceException;
import com.xie.common.core.utils.SpringUtils;
import com.xie.system.domain.SysClient;
import com.xie.web.domain.LoginVo;
/**
* @作者:xie
* @时间:2023/7/6 21:43
*/
public interface IAuthStrategy {
String BASE_NAME = "AuthStrategy";
static LoginVo login(LoginBody loginBody, SysClient sysClient) {
// 授权类型和客户端id
String clientId = loginBody.getClientId();
String grantType = loginBody.getGrantType();
String beanName = grantType + BASE_NAME;
if (!SpringUtils.containsBean(beanName)) {
throw new ServiceException("授权类型不正确!");
}
IAuthStrategy instance = SpringUtils.getBean(beanName);
instance.validate(loginBody);
return instance.login(clientId, loginBody, sysClient);
}
/**
* 参数校验
*/
void validate(LoginBody loginBody);
/**
* 登录
*/
LoginVo login(String clientId, LoginBody loginBody, SysClient client);
}
password登录策略
package com.xie.web.service.impl;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xie.common.core.constant.UserStatus;
import com.xie.common.core.domain.model.LoginBody;
import com.xie.common.core.domain.model.LoginUser;
import com.xie.common.core.enums.LoginType;
import com.xie.common.core.exception.UserException;
import com.xie.common.core.utils.ValidatorUtils;
import com.xie.common.core.validate.auth.PasswordGroup;
import com.xie.common.satoken.utils.LoginHelper;
import com.xie.system.domain.SysClient;
import com.xie.system.domain.SysUser;
import com.xie.system.domain.vo.SysUserVo;
import com.xie.system.mapper.SysUserMapper;
import com.xie.web.domain.LoginVo;
import com.xie.web.service.IAuthStrategy;
import com.xie.web.service.SysLoginService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @作者:xie
* @时间:2023/7/6 22:20
*/
@Slf4j
@Service("password" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class PasswordAuthStrategy implements IAuthStrategy{
private final SysUserMapper userMapper;
private final SysLoginService loginService;
@Override
public void validate(LoginBody loginBody) {
ValidatorUtils.validate(loginBody, PasswordGroup.class);
}
@Override
public LoginVo login(String clientId, LoginBody loginBody, SysClient client) {
String password = loginBody.getPassword();
String username = loginBody.getUsername();
SysUserVo user = loadByUserName(username);
loginService.checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, user.getPassword()));
// 此处可根据登录用户的数据不同 自行创建 loginUser
LoginUser loginUser = loginService.buildLoginUser(user);
SaLoginModel model = new SaLoginModel();
model.setDevice(client.getDeviceType());
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
// 例如: 后台用户30分钟过期 app用户1天过期
model.setTimeout(client.getTimeout());
model.setActiveTimeout(client.getActiveTimeout());
// 生成token
LoginHelper.login(loginUser, model);
LoginVo loginVo = new LoginVo();
loginVo.setAccessToken(StpUtil.getTokenValue());
loginVo.setExpireIn(StpUtil.getTokenTimeout());
return loginVo;
}
private SysUserVo loadByUserName(String username) {
SysUser sysUser = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
.select(SysUser::getUserName,SysUser::getStatus)
.eq(SysUser::getUserName,username));
if (ObjectUtil.isNull(sysUser)) {
log.info("登录用户:{} 不存在.", username);
throw new UserException("user.not.exists", username);
} else if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) {
log.info("登录用户:{} 已被停用.", username);
throw new UserException("user.blocked", username);
}
return userMapper.selectUserByUserName(username);
}
}