物业项目-笔记总结

项目开发前的准备

1. 编写mybatisPlus配置类

        本项目分页使用mybatisPlus的分页功能。(pageHelper也挺好用,想用这个可以自己网上查询用法)

@Configuration
@MapperScan("com.zw.web.*.mapper")
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

2.编写MybatisPlus元数据处理器

        自动写入创建时间,创建人,更新时间,更新者。便于记录数据操作

@Repository
@Slf4j
public class MyBatisPlusHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
        this.setFieldValByName("updateTime",LocalDateTime.now(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ...");
        this.setFieldValByName("updateTime",LocalDateTime.now(),metaObject);
    }
}

3.配置redis模板类,配置连接。

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
        // 创建RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置连接工厂
        template.setConnectionFactory(connectionFactory);
        // 创建JSON序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer =
                new GenericJackson2JsonRedisSerializer();
        // 设置Key的序列化
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        // 设置Value的序列化
        template.setValueSerializer(jsonRedisSerializer);
        template.setHashValueSerializer(jsonRedisSerializer);
        // 返回
        return template;
    }
}
spring:
  redis:
    host: 192.168.184.129
    port: 6379
    password: 123456
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: 100ms

4.整合Swagger2(较好用),配置文件兼容问题

@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean//设置接口框架的基本信息
    public Docket createRestApi(){
        return new Docket(DocumentationType.SWAGGER_2)
                //接口描述信息
                .apiInfo(apiInfo())
                .select()
                //扫描哪些包创建接口文档
                .apis(RequestHandlerSelectors.basePackage("top.psjj.wy"))
                .paths(PathSelectors.any())
                .build();
    }
    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .title("接口文档")
                .description("物业系统接口文档")
                .contact(new Contact("胖叔讲Java","http://127.0.0.1:8888/doc.html","xxx@xx.com"))
                .version("3.1")
                .build();
    }
}
spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

5.配置跨域工具类

        相比在类上写注解来跨域(@Crossorigin),配置类跨域可以一劳永逸。

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter(){
        CorsConfiguration config = new CorsConfiguration();
        //接受任意域名的请求
        config.addAllowedOrigin("*");
        //不支持提交COOKIE数据
        config.setAllowCredentials(false);
        //绑定请求头信息,使用通配符*接受任意字段
        config.addAllowedHeader("*");
        //支持任意提交方法
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**",config);
        return new CorsFilter(configSource);
    }
}

6.返回结果集封装

        可以统一响应结果,前后端交互起来更简便。

@Data
public class Result<T> {
    //状态码
    private Integer code;
    //响应消息
    private String msg;
    //响应数据
    private T data;
    //响应成功
    private static final int OK_CODE = 200;
    //响应成功消息
    private static final String OK_MSG = "SUCCESS";
    //响应失败
    private static final int SERVER_ERROR = 500;
    private static final String SERVER_ERROR_MSG = "服务器异常";
    //未认证
    private static final int UNAUTHORIZED = 401;
    private static final String UNAUTHORIZED_MSG = "未认证";
    //权限不足
    private static final int PERMISSION_DENIED = 403;
    private static final String PERMISSION_DENIED_MSG = "权限不足";

    public Result(Integer code,String msg,T data){
        this.code = code;
        this.msg = msg;
        this.data= data;
    }
    public Result(Integer code,String msg){
        this.code = code;
        this.msg = msg;
    }
    //响应成功
    public Result(){
        this(OK_CODE,OK_MSG);
    }
    public static Result success(){
        return new Result();
    }
    public static <T> Result<T> success(T data){
        Result<T> result = new Result<>(OK_CODE,OK_MSG,data);
        return result;
    }
    public static Result error(Integer code,String msg){
        return new Result(code,msg);
    }
    public static Result error(){
        return new Result(SERVER_ERROR,SERVER_ERROR_MSG);
    }
    public static Result unauthorized(){
        return new Result(UNAUTHORIZED,UNAUTHORIZED_MSG);
    }
    public static Result permissionDenied(){
        return new Result(PERMISSION_DENIED,PERMISSION_DENIED_MSG);
    }
}

7.编写jwt工具类

        用于生成和获取token令牌及令牌中的信息。

@Data
@ConfigurationProperties(prefix = "jwt")
@Component
public class JwtUtils {
    //密钥
    private String secret;

    // 过期时间 毫秒
    private Long expiration;


    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String generateToken(Map<String, Object> claims) {
        Date expirationDate = new Date(System.currentTimeMillis() + expiration);
        return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
    }
    /**
     * 生成令牌
     *
     * @param userId 用户id
     * @return 令牌
     */
    public String generateToken(Integer userId,String username,Integer userType) {
        Map<String, Object> claims = new HashMap<>(4);
        //设置token分类
        claims.put(Claims.SUBJECT,"WUYE_USER");
        //设置私有信息,用户信息
        claims.put("userId",userId);
        claims.put("username",username);
        claims.put("userType",userType);
        //设置签发时间为当前时间
        claims.put(Claims.ISSUED_AT, new Date());
        return generateToken(claims);
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    public Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }



    /**
     * 从令牌中获取用户id
     *
     * @param token 令牌
     * @return 用户id
     */
    public Integer getUserIdFromToken(String token) {
        Integer userId;
        try {
            Claims claims = getClaimsFromToken(token);
            userId = (Integer) claims.get("userId");
        } catch (Exception e) {
            userId = null;
        }
        return userId;
    }
    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = (String)claims.get("username");
        } catch (Exception e) {
            username = null;
        }
        return username;
    }
    /**
     * 从令牌中获取用户类型
     *
     * @param token 令牌
     * @return 用户类型
     */
    public Integer getUserTypeFromToken(String token) {
        Integer userType;
        try {
            Claims claims = getClaimsFromToken(token);
            userType = (Integer)claims.get("userType");
        } catch (Exception e) {
            userType = null;
        }
        return userType;
    }
    /**
     * 刷新令牌
     *
     * @param token 原令牌
     * @return 新令牌
     */
    public String refreshToken(String token) {
        String refreshedToken;
        try {
            Claims claims = getClaimsFromToken(token);
            claims.put(Claims.ISSUED_AT, new Date());
            refreshedToken = generateToken(claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }
}
#jwt配置
jwt:
  secret: pangshujiangjava
  #30分钟过期
  expiration: 1800000

8.配置全局异常处理类

@RestControllerAdvice("top.psjj.wy")
@Slf4j
public class GlobalException {
    @ExceptionHandler(Exception.class)
    public Object handlerException(Exception e){
        log.error("系统异常"+e.getMessage());
        return Result.error(500,e.getMessage());
    }
}

9.项目提交git

        记住下面几个指令即可:git init   git add .     git commit -m'描述'     git pull  origin     git push origin。

登录模块:基于SpringSecurity

编写SecurityConfig配置类,其中包括注入密码加密器、认证管理器、过滤器链

 //注入过滤链
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        //关闭csrf()攻击防护
        http.csrf().disable();
        //允许跨域
        http.cors();
        //关闭iframe窗口防护
        http.headers().frameOptions().disable();
        //关闭session会话
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //配置认证过滤器
        http.addFilterAfter(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class);
        //配置所有请求必须认证
        http.authorizeRequests().anyRequest().authenticated();
        //配置认证失败处理
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
        return http.build();
    }

DaoAuthenticationProvider

  // 注入DaoAuthenticationProvider
    @Bean
    public DaoAuthenticationProvider authenticationProvider(){
        DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
        auth.setUserDetailsService(userDetailsService);
        
        auth.setPasswordEncoder(passwordEncoder());
        return auth;
    }

配置忽略路径

//配置忽略路径
    @Bean
    public WebSecurityCustomizer securityCustomizer() throws Exception{
        return (web) -> {
            web.ignoring().antMatchers("/api/captcha",
                    "/api/login",
                    "/doc.html",
                    "/webjars/**",
                    "/swagger-resources/**",
                    "/v2/api-docs/**");
        };
    }

后端具体实现

验证码实现

        1.使用CaptchaUtil工具类创建一个验证码图片

        2.获取验证码内容(code)和验证码ID,以前缀+ID为key,码为值存入到redis数据库中

        3.向前端响应数据,包括验证码ID和getImageBase64Data()方法生成的带前缀的验证码图片压缩的字符串。

 public Result captcha(){
        //1.获取验证码
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(200, 40, 4, 20);
        //获取验证码压缩图片
        String imageBase64Data = captcha.getImageBase64Data();
        //获取验证码内容
        String captchaCode = captcha.getCode();
        //获取验证码id
        String captchaId = UUID.randomUUID().toString();
        //2.将验证码信息存到redis中 key 为wy:login:captcha:+id redis中会以冒号为分割创建文件夹
        redisTemplate.opsForValue().set(RedisConstant.CAPTCHA_PRE+captchaId,
                captchaCode,RedisConstant.CAPTCHA_EXPIRE_TIME, TimeUnit.SECONDS);
 
        //3.响应数据
        HashMap<String,Object> map = new HashMap<>();
        map.put("captchaId",captchaId);
        map.put("imageBase64",imageBase64Data);
        return Result.success(map);
    }

jwt认证过滤

        1.继承OncePerRequestFilter类,重写doFilterInternal()方法

        2. 从请求头中获取token,如果没有token(未登录)放行,有就继续

        3.解析token(包括创建token需要的信息)

        4.刷新token和redis中存储的用户的有效期

        5.将用户信息存入securityContextHolder中,使得过滤链上每个环节都能通过SecurityContextHolder拿到用户信息。放行。

public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private JwtUtils jwtUtils;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //1.获取token
        String token = request.getHeader("authorization");
        //System.out.println(token);
        if (!StringUtils.hasText(token)){
            //放行
            filterChain.doFilter(request,response);
            return;
        }
        //2.解析token
        Integer userId;
        Integer userType;
        try {
             userId = jwtUtils.getUserIdFromToken(token);
             userType = jwtUtils.getUserTypeFromToken(token);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("token非法");
        }
 
        //3.刷新token和redis的有效期
        String refreshToken = jwtUtils.refreshToken(token);
        response.setHeader("Access-Control-Expose-Headers","Authorization");
        response.addHeader("Authorization",refreshToken);
 
        if (SystemConstant.USER_TYPE_WUZHU==userType){
 
            SysUser sysUser = (SysUser) redisTemplate.opsForValue().get(RedisConstant.LOGIN_SYSTEM_USER_PRE + userId);
            if (Objects.isNull(sysUser)){
                throw new RuntimeException("用户未登录");
            }
            //刷新redis有效期
            redisTemplate.expire(RedisConstant.LOGIN_SYSTEM_USER_PRE + userId, RedisConstant.LOGIN_SYSTEM_USER_EXPIRE_TIME, TimeUnit.MINUTES);
            //3.将用户信息存入securityContextHolder中
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(sysUser, null, null);
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            filterChain.doFilter(request,response);
        }else {
            LiveUser liveUser = (LiveUser) redisTemplate.opsForValue().get(RedisConstant.LOGIN_LIVE_USER_PRE + userId);
            if (Objects.isNull(liveUser)){
                throw new RuntimeException("用户未登录");
            }
            redisTemplate.expire(RedisConstant.LOGIN_LIVE_USER_PRE+userId,RedisConstant.LOGIN_LIVE_USER_EXPIRE_TIME,TimeUnit.MINUTES);
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(liveUser, null, null);
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            filterChain.doFilter(request,response);
        }
 
 
    }
}

登录接口

        返回result带有token令牌的map集合

实现UserDetailsService

        基于数据库查询用户名对应的用户信息

登录业务层

        1.校验验证码

        从redis中取出验证码和前端输入的验证码将进行比较,错误抛出异常

 private void validCaptcha(String captchaId, String captchaCode) {
        //1.从redis中获取验证码
        String captchaCode2 = (String) redisTemplate.opsForValue().get(RedisConstant.CAPTCHA_PRE + captchaId);
        if (!captchaCode.equalsIgnoreCase(captchaCode2)){
            throw new RuntimeException("验证码有误");
        }
    }

        2.校验用户名密码返回认证信息

                ①将用户名密码封装成usernamePasswordAuthenticationToken

                ②通过authenticationManager调用认证方法,返回认证对象,认证对象为空抛出异常,反之返回认证对象

private Authentication validUsernameAndPassword(String username, Integer userType, String password) {
        username = username+":"+userType;
        //将用户名密码封装成usernamePasswordAuthenticationToken
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, password);
        //进行认证
        Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        if(Objects.isNull(authentication)){
            throw new RuntimeException("用户名或密码错误");
        }
        return authentication;
 
    }

        3.将用户信息存入redis,并响应token数据

                1.用户信息在认证对象的主体中

                2.创建token返回result带有token令牌的map集合

 private Result responseToken(Integer userType, Authentication authentication) {
        int userId;
        String username;
        //用户类型为物主
        if (userType== SystemConstant.USER_TYPE_WUZHU){
            SysUser sysUser = (SysUser) authentication.getPrincipal();
            userId = sysUser.getUserId();
            username = sysUser.getUsername();
            //将物主信息存到redis中
            redisTemplate.opsForValue().set(RedisConstant.LOGIN_SYSTEM_USER_PRE + userId, sysUser, RedisConstant.LOGIN_SYSTEM_USER_EXPIRE_TIME, TimeUnit.MINUTES);
 
        }else {
            LiveUser liveUser = (LiveUser) authentication.getPrincipal();
            userId = liveUser.getUserId();
            username = liveUser.getUsername();
            //将业主信息存入redis
            redisTemplate.opsForValue().set(RedisConstant.LOGIN_LIVE_USER_PRE + userId, liveUser, RedisConstant.LOGIN_LIVE_USER_EXPIRE_TIME, TimeUnit.MINUTES);
        }
        //创建token
        String token = jwtUtils.generateToken(userId, username, userType);
        HashMap<String, String> map = new HashMap<>();
        map.put("token",token);
        return Result.success(map);
    }

前端具体实现

        1.验证码获取,将返回结果绑定到img标签的src属性中,带有前缀会自动解压

        2.login() 方法,将响应结果中的token存储到session storage中

        3.配置http.js

                ①请求之前的拦截器,从session storage中获取token添加到请求头中(key为后端jwt过滤器中获取token的key)

//请求发送之前的拦截器
axios.interceptors.request.use(
  config => {
    let token = sessionStorage.getItem("authorization")
    //如果token存在,把token添加到请求的头部
    if (token) {
      config.headers['authorization'] = token
    }
    return config
  },
  error => {
    console.log(error)
    return Promise.reject(error)
  }
)

        ②请求返回之后的处理,从响应头中获取token并更新session storage中的token(前端刷新token)

 
//请求返回之后的处理
axios.interceptors.response.use(
 
  response => {
    if(response.headers.authorization){
      sessionStorage.setItem("authorization",response.headers.authorization)
  }
    const res = response.data
 
    if (res.code !== 200) {
      Message({
        message: res.msg || '服务器出错',
        type: 'error',
        duration: 5 * 1000
      })
      return Promise.reject(new Error(res.msg || '服务器出错'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) 
    Message({
      message: error.msg || '服务器出错!',
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

用户管理模块

角色管理和菜单管理模块

权限管理模块

房屋管理模块

物业系统管理模块

动态菜单

密码加密传输

3.1 节 加密的几种方式

在Java开发的过程中,很多场景下都需要加密解密,比如对敏感数据的加密,对配置文件信息的加密,通信数据的加密等等。

加密分为三类:

  1. 摘要加密(digest)

  2. 对称加密(symmetric)

  3. 非对称加密(asymmetric)

3.1.1 摘要加密(digest)

说明:数字摘要是将任意长度的消息变成固定长度的短消息,它类似于一个自变量是消息的函数,也就是Hash函数。数字摘要就是采用单向Hash函数将需要加密的明文“摘要”成一串固定长度(128位)的密文这一串密文又称为数字指纹,它有固定的长度,而且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。 常见的有:

  1. MD5

  2. SHA

  3. 16进制编码

  4. Base64编码

3.1.2 对称加密算法(symmetric)

对称加密算法是应用较早的加密算法,技术成熟。在对称加密算法中,数据发信方将明文(原始数据)和加密密钥(mi yao)一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。

常见的有:

  1. DES

  2. AES(新)

4.1.3 非对称加密 (asymmetric)

 非对称加密算法:又称为公开密钥加密算法,需要两个密钥,一个为公开密钥(PublicKey)即公钥,一个为私有密钥(PrivateKey)即私钥。两者需要配对使用。用其中一者加密,则必须用另一者解密。

常见的有:

  1. RSA 算法

  2. 数字签名

3.2 节 常见加密工具类使用

Hutool-all包含Hutool-crypto模块,hutool-crypto中包含创建的算法工具类,具体如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C语言是一种广泛应用于计算机科学和软件开发的编程语言。它具有强大的功能和灵活性,适用于开发各种类型的应用程序。 C语言专题精讲篇是一个对C语言进行深入学习和讲解的系列文章或课程。它汇总了C语言相关的重要知识点和技巧,旨在帮助学习者更好地理解和运用C语言。 这个专题中的笔记涵盖了C语言的各个方面,包括基本语法、数据类型、运算符、流程控制、函数、数组、指针、结构体、文件操作等。通过系统性的学习和总结,这些笔记可以帮助学习者逐步掌握C语言的核心概念和常用技巧。 在这个专题中,学习者可以学到如何编写简单的C程序,如何使用变量和运算符进行计算,如何使用条件和循环语句控制程序流程,如何使用函数进行代码的模块化,如何使用数组和指针进行数据的处理,如何使用结构体组织复杂数据,如何进行文件的读写等等。 C语言专题精讲篇的目的是帮助学习者全面、深入地了解C语言的各个方面,并能够独立编写和调试简单到中等难度的C程序。通过反复实践和练习,学习者可以逐渐提高自己的编程能力,并为进一步学习更高级的编程语言打下坚实的基础。 总之,C语言专题精讲篇的笔记汇总是一份重要的学习资料,可以帮助学习者系统地学习和掌握C语言的基础知识和常用技巧,为他们未来的编程之路打下坚实的基石。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值