CREATE TABLE `sys_user` ( `user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `sex` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `email_verified` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `true_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `id_card` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `birthday` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `introduction` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `organization_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `state` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `deleted` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `create_time` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `update_time` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
注意的是,password之后使用BCryptPasswordEncoder加密,因此password字符长度必须大于60
二、UserEnrity层package com.epwcloud.common.system.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Date;
import java.util.List;
@ApiModel(description = "用户")
@TableName("sys_user")
public class User implements UserDetails {
@ApiModelProperty("用户id")
@TableId(value = "user_id", type = IdType.AUTO)
private Integer userId;
@ApiModelProperty("账号")
private String username;
@ApiModelProperty("密码")
private String password;
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("头像")
private String avatar;
@ApiModelProperty("性别")
private Integer sex;
@ApiModelProperty("手机号")
private String phone;
@ApiModelProperty("邮箱")
private String email;
@ApiModelProperty("邮箱是否验证,0否,1是")
private Integer emailVerified;
@ApiModelProperty("真实姓名")
private String trueName;
@ApiModelProperty("身份证号")
private String idCard;
@ApiModelProperty("出生日期")
private Date birthday;
@ApiModelProperty("个人简介")
private String introduction;
@ApiModelProperty("机构id")
private Integer organizationId;
@ApiModelProperty("状态,0正常,1冻结")
private Integer state;
@ApiModelProperty("是否删除,0否,1是")
@TableLogic
private Integer deleted;
@ApiModelProperty("注册时间")
private Date createTime;
@ApiModelProperty("修改时间")
private Date updateTime;
@ApiModelProperty("性别名称")
@TableField(exist = false)
private String sexName;
@ApiModelProperty("机构名称")
@TableField(exist = false)
private String organizationName;
@ApiModelProperty("角色id")
@TableField(exist = false)
private List roleIds;
@ApiModelProperty("角色列表")
@TableField(exist = false)
private List roles;
@ApiModelProperty("权限列表")
@TableField(exist = false)
private List
三、Controller层
public class UserController extends baseController {
@Autowired
private UserService userService;
@Autowired
private MenuService menuService;
@ApiOperation("用户登录")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "账号", required = true, dataType = "string", paramType = "query"),
@ApiImplicitParam(name = "password", value = "密码", required = true, dataType = "string", paramType = "query"),
})
@PostMapping("/login")
public void login(String username, String password) {
// 登录操作由JwtLoginFilter完成
}
@PreAuthorize("hasAuthority('sys:user:save')")
@OperLog(value = "用户管理", desc = "添加", param = false, result = true)
@ApiOperation("添加用户")
@PostMapping()
public JsonResult add(@RequestBody User user) {
user.setState(0);
user.setPassword(userService.encodePsw(user.getPassword()));
if (userService.saveUser(user)) {
return JsonResult.ok("添加成功");
}
return JsonResult.error("添加失败");
}
}
四、JwtLoginFilter、server、serverImpl层
package com.epwcloud.common.core.security;
import com.alibaba.fastjson.JSON;
import com.epwcloud.common.core.Constants;
import com.epwcloud.common.core.web.JsonResult;
import com.epwcloud.common.system.entity.LoginRecord;
import com.epwcloud.common.system.service.LoginRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
private LoginRecordService loginRecordService;
public JwtLoginFilter(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
super.setFilterProcessesUrl("/api/login");
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authResult)
throws IOException, ServletException {
UserDetails user = (UserDetails) authResult.getPrincipal();
String access_token = JwtUtil.buildToken(user.getUsername(), Constants.TOKEN_EXPIRE_TIME, Constants.TOKEN_KEY);
// 记录登录日志
loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_LOGIN, null, request);
// 返回json数据
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(JSON.toJSonString(JsonResult.ok("登录成功").put("access_token", access_token)
.put("token_type", JwtUtil.TOKEN_TYPE)));
out.flush();
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
String username = request.getParameter("username");
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
JsonResult result;
if (e instanceof UsernameNotFoundException) {
result = JsonResult.error("账号不存在");
loginRecordService.saveAsync(username, LoginRecord.TYPE_ERROR, "账号不存在", request);
} else if (e instanceof BadCredentialsException) {
result = JsonResult.error("账号或密码错误");
loginRecordService.saveAsync(username, LoginRecord.TYPE_ERROR, "账号或密码错误", request);
} else if (e instanceof LockedException) {
result = JsonResult.error("账号被锁定");
loginRecordService.saveAsync(username, LoginRecord.TYPE_ERROR, "账号被锁定", request);
} else {
result = JsonResult.error(e.getMessage());
}
out.write(JSON.toJSonString(result));
out.flush();
}
}
boolean saveUser(User user);
String encodePsw(String psw);
@Transactional
@Override
public boolean saveUser(User user) {
if (user.getUsername() != null && baseMapper.selectCount(new QueryWrapper()
.eq("username", user.getUsername())) > 0) {
throw new BusinessException("账号已存在");
}
if (user.getPhone() != null && baseMapper.selectCount(new QueryWrapper()
.eq("phone", user.getPhone())) > 0) {
throw new BusinessException("手机号已存在");
}
if (user.getEmail() != null && baseMapper.selectCount(new QueryWrapper()
.eq("email", user.getEmail())) > 0) {
throw new BusinessException("邮箱已存在");
}
boolean result = baseMapper.insert(user) > 0;
if (result) {
addUserRoles(user.getUserId(), user.getRoleIds(), false);
}
return result;
}
@Override
public String encodePsw(String psw) {
if (psw == null) return null;
return new BCryptPasswordEncoder().encode(psw);
}
五、Mapper层、MapperXML层
此处用的是MybatisPlus,mabber继承了baseMabber,所以基础的CURD不需要再写mabber
六、放行接口登录页面JwtLoginFilter配置已经自动放行了,需要在SecurityConfig中放行/api/sys/user
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers(HttpMethod.GET, "/favicon.ico", "/api/file/**", "/", "/assets/**", "/**.html")
.permitAll()
.antMatchers("/api/login","/api/sys/user", "/error", "/druid/**", "/webjars/**", "/swagger-ui.html",
"/swagger-resources/**", "/v3/api-docs","/doc.html").permitAll()
.anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().headers().frameOptions().disable()
.and().cors().and().csrf().disable();
http.exceptionHandling().accessDeniedHandler(jwtExceptionHandler()).authenticationEntryPoint(jwtExceptionHandler());
http.logout().logoutUrl("/logout").logoutSuccessHandler(jwtLogoutSuccessHandler());
http.addFilterBefore(jwtLoginFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(jwtRequestFilter(), UsernamePasswordAuthenticationFilter.class);
}



