springboot用户模块开发汇总

概览:用户注册,登录,登出模块,控制台日志输出,json||表单接收区别,返回类型构造,异常返回模块,参数校验模块,session模块,优化:登录拦截器实现

开发顺序推荐:Dao->Service->controller

请求头类型选择

请求使用一般Content-Type:application/json类型传输,少数使用form格式。

两种接收方式区别:

    /**
     * form表单方式接收数据
     * @param username
     */
    @PostMapping("/register")
    public void register(@RequestParam(value = "username") String username){}
    @PostMapping("/register2")
    public void register2(User user){}

    /**
     * json方式接收数据
     */
    @PostMapping("/register3")
    public void register3(@RequestBody User user){}

返回类型构建

返回对象vo

@Data
@JsonInclude(value = JsonInclude.Include.NON_NULL)
public class ResponseVo<T> {
    private Integer status;
    private String msg;
    private T data;

    public ResponseVo(Integer status, String msg) {
        this.status = status;
        this.msg = msg;
    }

    public ResponseVo(Integer status, T data) {
        this.status = status;
        this.data = data;
    }

    /**
     * 构建常用返回类型的静态方法以供直接调用
     *
     * @return
     */
    public static <T> ResponseVo<T> success() {
        return new ResponseVo<>(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getDesc());
    }

    public static <T> ResponseVo<T> success(String msg) {
        return new ResponseVo<>(ResponseEnum.SUCCESS.getCode(), msg);
    }

    public static <T> ResponseVo<T> successByCommonData(T data) {
        return new ResponseVo<>(ResponseEnum.SUCCESS.getCode(), data);
    }

    public static <T> ResponseVo<T> error(ResponseEnum responseEnum) {
        return new ResponseVo<>(responseEnum.getCode(), responseEnum.getDesc());
    }

    public static <T> ResponseVo<T> error(ResponseEnum responseEnum,String msg) {
        return new ResponseVo<>(responseEnum.getCode(), msg);
    }

    public static <T> ResponseVo<T> error(ResponseEnum responseEnum, BindingResult bindingResult) {
        return new ResponseVo<>(responseEnum.getCode(), bindingResult.getFieldError().getField() + bindingResult.getFieldError().getDefaultMessage());
    }

}

 返回类型enum

@Getter
public enum ResponseEnum {
    SUCCESS(0, "成功"),
    FAIL_PSWERROR(1, "密码错误"),
    FAIL_USER_EXIST(2, "用户已存在"),
    FAIL_NON_LOGIN(10, "用户为未登录"),
    MASHINE_ERROR(-1, "服务器错误"),
    ;

    int code;
    String desc;

    ResponseEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}

构建异常返回类并设置状态码:

@ControllerAdvice
public class RunTimeExceptionJandler {
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)  //设置相应状态码
    public ResponseVo handle(RuntimeException e) {
        return ResponseVo.error(ResponseEnum.MASHINE_ERROR, e.getMessage());
    }
}

参数校验模块:

@Data
public class UserForm {

    @NotBlank(message = "用户名不能为空")  //用于字符String,会判断空格
    //@NotNull  判断是否为null
    //@NotEmpty  用于集合,数组,List判空
    private String username;
    @NotBlank
    private String password;
    @NotBlank
    private String email;

}

/**
 * json方式接收数据
 * @Valid & BindingResult: 用于判断参数合法性校验
 */
@PostMapping("/register3")
public ResponseVo register3(@Valid @RequestBody UserForm userForm, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        System.out.println("注册参数有无,{}"+bindingResult.getFieldError().getField()+bindingResult.getFieldError().getDefaultMessage());
        return ResponseVo.error(ResponseEnum.PARAMS_ERROR,bindingResult.getFieldError().getField()+bindingResult.getFieldError().getDefaultMessage());
    }
    return ResponseVo.success("注册成功");
}

控制台日志输出

mybatis:
  configuration:
    map-underscore-to-camel-case: true
    #控制台日志配置
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/*.xml
logging: #输出日志格式
  pattern:
    console: "[%thread] %-5level %logger{36} - %msg%n"

用户注册模块:

用户实体类

@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;
    private String phone;
    private String question;
    private String answer;
    private Integer role;
    private Date createTime;
    private Date updateTime;
}

用户注册功能开发

@Service
public class IUserService {

    @Autowired
    private UserMapper userMapper;

    /**
    注册
     **/
    void register(User user){
        //用户名不能重复
        if(userMapper.countByUsername(user.getUsername())>0){
            throw new RuntimeException("该username已注册");
        }
        //email不能重复
        if(userMapper.countByUsername(user.getUsername())>0){
            throw new RuntimeException("该邮箱已注册");
        }
        //MD5加密摘要算法(Spring自带)
         user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes(StandardCharsets.UTF_8)));
        //用户类别设置
        user.setRole(0);
        //写入数据库
        int result = userMapper.insertSelective(user);
        if(result == 0){
            throw new RuntimeException("注册失败");
        }
    }
}

数据库操作

@Service
public interface UserMapper {

    @Select("select count(1) from mall_user where username = #{username}")
    int countByUsername(String username);

    @Select("select count(1) from mall_user where email = #{email}")
    int countByEmail(String email);

    @Update("insert into mall_user * values (user.)")
    int insertSelective(User user);
}

用户登录并设置session模块:

    @PostMapping("/login")
    public ResponseVo<User> login(@Valid @RequestBody UserLoginForm userLoginForm, BindingResult bindingResult, HttpServletRequest httpServletRequest) {
        if (bindingResult.hasErrors()) {
            return ResponseVo.error(ResponseEnum.PARAMS_ERROR, bindingResult);
        }
        ResponseVo<User> userResponseVo = userService.login(userLoginForm.getUsername(), userLoginForm.getPassword());
        //设置Session
        HttpSession session = httpServletRequest.getSession();
        session.setAttribute(UserConst.CURRENT_USER, userResponseVo);
        return userResponseVo;
    }

    @GetMapping("/getUser")
    public ResponseVo<User> getUserInfo(HttpServletRequest request){
        User user = (User) request.getSession().getAttribute(UserConst.CURRENT_USER);
        if(user == null){
            return ResponseVo.error(ResponseEnum.FAIL_NON_LOGIN);
        }
        return ResponseVo.successByCommonData(user);
    }
public class UserConst {
    public static final String CURRENT_USER = "currentUser";
}
    /**
    登录
     **/
    public ResponseVo<User> login(String username,String password){
        //推荐只使用用户名去查,因为有索引,密码没有
        User user = userMapper.selectByUsername(username, password);
        if(user == null){
            return ResponseVo.error(ResponseEnum.ERROR_USER_PSW);
        }
        if(!user.getPassword().equalsIgnoreCase(DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)))){
            return ResponseVo.error(ResponseEnum.ERROR_USER_PSW);
        }
        return ResponseVo.success();
    }

注意:推荐只使用用户名去查,因为有索引,密码没有

Session和cookie的区别:

session保存在内存,重启会丢失。分布式改进版本:token+redis

cookie保存在客户端浏览器中。

注意:cookie跨域问题(localhost和127.0.0.1是不同的域名和地址,也算跨域),只有同一个域名下,cookie才能保存session的生效。但是如果篡改成已登录的的sessionId,任意机器也能登录成功。

详解:分布式会话和基于TOKEN的分布式会话_xy294636185的博客-CSDN博客

登出功能实现:

    @PostMapping("/logout")
    public ResponseVo logout(HttpServletRequest request){
        User user = (User) request.getSession().getAttribute(UserConst.CURRENT_USER);
        if(user == null){
            return ResponseVo.error(ResponseEnum.FAIL_NON_LOGIN);
        }
        request.getSession().removeAttribute(UserConst.CURRENT_USER);
        return ResponseVo.success();
    }

登录失效原因:

1.cookie中SessionId变化

2.服务器重启

3.session过期(如果不过期需要redis+token实现无限延期

#配置session过期时间
server:
  servlet:
    session:
      timeout: 60 #单位s,底层实现设置了最低限制,不能低于1min

优化:登录状态的统一判断-拦截器功能

两种方式实现:

1.Interceptor-Url(从http层面进行拦截)

public class UserLoginInterceptor implements HandlerInterceptor {

    /**
     * 在方法前拦截
     * true表示继续流程,false表示中断
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        User user = (User) request.getSession().getAttribute(UserConst.CURRENT_USER);
        if(user == null){
            //返回方式1,不推荐
            //response.getWriter().print("error");
            //返回方式2,统一异常处理
            throw new UserLoginException();
//            return false;
        }
        return true;
    }

//    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
//    }
//
//    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
//    }
}
//开启拦截
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    //这里可以写进配置类中统一配置
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserLoginInterceptor())
                .addPathPatterns("/**")   //默认拦截所有请求
                .excludePathPatterns("/error","user/login","user/register");  //排除登录和注册接口
    }
}

注意:不要拦截错误页面:"/error"

特殊异常处理

public class UserLoginException extends RuntimeException{
    //这里什么都不用写,只需要类名,抛完一异常会被统一异常处理拦截
}
@ControllerAdvice
public class RunTimeExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)  //设置相应状态码
    public ResponseVo handle(RuntimeException e) {
        return ResponseVo.error(ResponseEnum.MASHINE_ERROR, e.getMessage());
    }

    //捕获异常
    @ExceptionHandler(UserLoginException.class)
    @ResponseBody
    public ResponseVo userLoginHandle(){
        return ResponseVo.error(ResponseEnum.FAIL_NON_LOGIN);
    }
}

2.AOP-包名

带特殊用户版本:https://blog.csdn.net/xy294636185/article/details/119331342

简单版:

@Aspect
@Component
public class AuthVerifyAop {

    @Value("${oauth.user_cache_key}")
    private String userCacheKey;

    @Pointcut("@annotation(com.fii.eodc.intellect.decistionbrain.config.auth.AuthApis)")
    public void doOperation() {
    }

    @Before("doOperation()&&@annotation(authApis)")
    public void before(JoinPoint point, AuthApis authApis) {
        //获取请求路由
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        assert requestAttributes != null;
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        String requestURI = request.getRequestURI();
        //获取用户信息
        List<PermissionVO> permission = (List<PermissionVO>) request.getSession().getAttribute("permission");
        if (permission != null && permission.size() > 0) {
            //通过注解获取被访问接口可允许的权限ID
            String[] value = authApis.value();
            if (value.length == 0) {
                //通过路由获取被访问接口可允许的权限ID
                value = ApiAuthVerify.getResourceIds(requestURI);
            }
            if (value == null || value.length == 0) {
                throw new GlobalException(Result.message(403, "unauthorized"));
            }
        }
        throw new GlobalException(Result.message(403, "unauthorized"));
    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface AuthApis {
    String[] value() default {};
    String desc() default "";
}
public enum  ApiAuthVerify {

    /*Demo*/
    SYS_DEMO("/demo/getDemo",new String[]{"SYS_DAMO"}),
    SYS_DEMO_SECOND("/demo/getDemo2",new String[]{"SYS_DEMO_SECOND"})
    ;
    private String uri;
    private String[] resourceIds;

    ApiAuthVerify(String uri, String[] resourceIds){
        this.uri = uri;
        this.resourceIds = resourceIds;
    }

    public static String[] getResourceIds(String uri){
        for (ApiAuthVerify verify : ApiAuthVerify.values()) {
            if(Objects.equals(verify.uri,uri))
                return verify.resourceIds;
        }
        return null;
    }

    public String getUri(){
        return uri;
    }

    public String[] getResourceIds(){
        return resourceIds;
    }
}
@Api(tags = "demo")
@RestController
@RequestMapping("/demo")
public class DemoController {

    @Autowired
    private DemoService demoService;

    @ApiOperation(value = "demo",httpMethod = "GET",notes = "demo")
    @GetMapping("/getDemo")
    @AuthApis("SYS_DAMO_SECOND")
    public Result<List<DemoVO>> getDemo(){
        return Result.success(demoService.getDemo());
    }

    @ApiOperation(value = "demo2",httpMethod = "GET",notes = "demo2")
    @GetMapping("/getDemo2")
    @AuthApis("SYS_DAMO")
    public Result<List<DemoVO>> getDemo2(){
        return Result.success(demoService.getDemo2());
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值