实习笔记2

改进之前做的小项目

需求

1、优化前端页面(尽量居中显示)
2、加入Redis
3、加入图形验证码

实现过程

1)service层优化

之前直接在controller层中使用UserMapper,不符合开发规范。现在要在service层调用UserMapper,然后在controller层中调用Service层。
改进后的目录结构
在这里插入图片描述
UserService

public interface UserService {
    List<User> searchAllUsers();
    int searchId(int id);
    int searchByName(String name);
}

UserServiceImpl

public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    //搜索所有的User
    @Override
    public List<User> searchAllUsers() {
        List<User> userList = userMapper.selectList(null);
        return userList;
    }

    //搜索当前自增Id
    @Override
    public int searchId(int id) {
        return userMapper.searchId();
    }

    //通过username查找user,返回结果为人数
    @Override
    public int searchByName(String username) {
        return userMapper.searchByName(username);
    }
}

2)通用返回格式类

在实际开发中,后端通常只返回给前端一个json格式的数据串,所有的信息都包含在这条json串中,一般会自定义一种数据格式用来保存需要传递的信息,其格式通常包含状态码,状态信息,以及数据。
在entity包中新建类ResultData

//此类为通用的返回格式
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultData implements Serializable {
    public static final int SUCCESS_CODE = 200;
    public static final int FAIL_CODE = 0;
    public static final int ERROR_CODE = -1;

    public static final String SUCCESS_MSG = "成功";
    public static final String FAIL_MSG = "业务失败";
    public static final String ERROR_MSG = "系统异常";

    private Integer code;
    private String msg;
    private List<?> data;

    public static ResultData success(){
        return new ResultData(SUCCESS_CODE,SUCCESS_MSG,null);
    }

    public static ResultData success(String msg,List<?> data){
        return new ResultData(SUCCESS_CODE,msg,data);
    }

    public static Object success(String msg) {
        return new ResultData(SUCCESS_CODE,msg,null);
    }

    public static ResultData fail(){
        return new ResultData(FAIL_CODE,FAIL_MSG,null);
    }

    public static ResultData fail(String msg,List<?> data){
        return new ResultData(FAIL_CODE,msg,data);
    }

    public static ResultData fail(String s) {
        return new ResultData(FAIL_CODE,s,null);
    }

    public static ResultData error(){
        return new ResultData(ERROR_CODE,ERROR_MSG,null);
    }


}

将通用的返回格式定义好后,对之前的代码进行优化,将大部分的返回格式定义为如下的形式

return JSON.toJSONString(ResultData.fail("验证码输入错误,请重试"));

其controller方法上应当增加注解@ResponseBody
由于大部分方法的返回格式都为String,所以还可以将以上的结果进一步封装,这里不做演示

3)加入验证码

这里使用的第三方包是kaptcha,这里的步骤参考了https://zhuanlan.zhihu.com/p/280784782
首先引入依赖

        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>

然后配置类

@Configuration
public class KaptchaConfig {

    @Bean
    public DefaultKaptcha getDefaultKaptcha(){
        DefaultKaptcha captchaProducer = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", "no");
        properties.setProperty("kaptcha.border.color", "105,179,90");
        properties.setProperty("kaptcha.textproducer.font.color", "blue");
        properties.setProperty("kaptcha.image.width", "110");
        properties.setProperty("kaptcha.image.height", "36");
        properties.setProperty("kaptcha.textproducer.font.size", "30");
        properties.setProperty("kaptcha.session.key", "code");
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCEFGHIJKLMNOPQRSTUVWXYZ");
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
        properties.setProperty("kaptcha.noise.color", "black");
//        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.DefaultNoise");
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
        properties.setProperty("kaptcha.background.clear.from", "232,240,254");
        properties.setProperty("kaptcha.background.clear.to", "232,240,254");
        properties.setProperty("kaptcha.textproducer.char.space", "3");
        Config config = new Config(properties);
        captchaProducer.setConfig(config);
        return captchaProducer;
    }
}

在配置类中可以配置验证码的大小、颜色、字体等等
写kaptchaController,用于生成验证码

@Controller
public class KaptchaController {

    @Autowired
    private Producer kaptchaProducer = null;

    //生成
    @RequestMapping("/kaptcha")
    public void getKaptchaImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
        HttpSession session = request.getSession();
        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/jpeg");
        //生成验证码
        String capText = kaptchaProducer.createText();
        session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);
        //向客户端写出
        BufferedImage bi = kaptchaProducer.createImage(capText);
        try(ServletOutputStream out = response.getOutputStream()){
            ImageIO.write(bi, "jpg", out);
        }
    }
    
}

随后修改前端页面,login.html之前使用的是表单提交,现在改为普通的table,登录及注册功能绑定点击事件,现在要将验证码加入,需要在table中新增一行

另外,之前用户名或者密码输入错误时,直接跳转到error界面,现在此界面上的操作都由ajax完成,具体的修改如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>

<table align="center" style="vertical-align: middle">
    <tr>
        <td>用户名:</td>
        <td><input type="text" id="username"></td>
    </tr>
    <tr>
        <td>密码:</td>
        <td><input type="password" id="password"></td>
    </tr>
    <tr>
        <td>
            <span class="input-group-btn">
                <img id="captcha_img" alt="验证码" title="点击更换" onclick="refreshKaptcha()" src="/kaptcha" />
            </span>
        </td>
        <td class="input-group">
            <input class="form-control" type="text" autocomplete="new-password" placeholder="验证码" required maxlength="4"
                   id="verifyCode">
        </td>
    </tr>
    <tr>
        <td align="center"><button onclick="login()">登录</button></td>
        <td align="center"><button onclick="register()">注册</button></td>
    </tr>
</table>

</body>
</html>
<script>

    function login() {
        var username = document.getElementById("username").value;
        var password = document.getElementById("password").value;
        var verifyCode = document.getElementById("verifyCode").value;
        $.ajax({
            type: "POST",
            url: "http://localhost:8080/login",
            data: {username: username, password: password,verifyCode: verifyCode},
            success: function(response) {
                var res = JSON.parse(response);
                window.alert(res.msg);
                if(res.code === 200){
                    window.location.href = "http://localhost:8080/userList/";
                }
                // 处理成功响应的回调函数
            },
            error: function(xhr, status, error) {
                window.alert("系统异常,登录失败");
                window.location.href = "http://localhost:8080/";
                // 处理错误响应的回调函数
            }
        });
    }

    function register() {
        var username = document.getElementById("username").value;
        var password = document.getElementById("password").value;
        var verifyCode = document.getElementById("verifyCode").value;
        $.ajax({
            type: "POST",
            url: "http://localhost:8080/register",
            data: {username: username, password: password,verifyCode: verifyCode},
            success: function(response) {
                var res = JSON.parse(response);
                window.alert(res.msg);
                if(res.code === 200){
                    window.location.href = "http://localhost:8080/userList";
                }
                // 处理成功响应的回调函数
            },
            error: function(xhr, status, error) {
                window.alert("系统异常,注册失败");
                window.location.href = "http://localhost:8080/";
                // 处理错误响应的回调函数
            }
        });
    }

    //刷新验证码
    function refreshKaptcha() {
        document.getElementById('captcha_img').src="/kaptcha?"+ Math.random();
    }
</script>

同时,修改controller中对应的方法
register

@PostMapping("/register")
    @ResponseBody
    public String register(@RequestParam("username") String username,
                           @RequestParam("password") String password,
                           @RequestParam("verifyCode") String verifyCode,
                           Model model,
                           HttpServletResponse response,
                           HttpServletRequest request){
        //非空验证
        if(StringUtils.isEmptyOrWhitespace(username) || StringUtils.isEmptyOrWhitespace(password)) {
            return JSON.toJSONString(ResultData.fail("用户名或密码不能为空,请重试"));
        }
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);

        //检验验证码输入
        String kaptchaCode = (String) request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
        if(!StringUtils.equalsIgnoreCase(verifyCode,kaptchaCode)){
            return JSON.toJSONString(ResultData.fail("验证码输入错误,请重试"));
        }

        //验证用户名
        int n = userService.searchByName(username);
        if(n > 0){
            return JSON.toJSONString(ResultData.fail("该用户已存在,请重试"));
        }

        //插入数据库
        int res = userService.insert(user);
        //获取自增ID
        int id = userService.searchId();
        user.setId(id);
        if(res > 0){
            Cookie cookie = new Cookie("userId",id+"");
            cookie.setMaxAge(60 * 60 * 24 * 7);//7天有效
            response.addCookie(cookie);
            return JSON.toJSONString(ResultData.success("注册成功",userService.searchAllUsers()));
        }else{
            return JSON.toJSONString(ResultData.fail("系统异常,请重试"));
        }

    }

login

@PostMapping("/login")
    @ResponseBody
    public String login(@RequestParam("username") String username,
                         @RequestParam("password") String password,
                         @RequestParam("verifyCode") String verifyCode,
                         Model model,
                         HttpServletResponse response,
                         HttpServletRequest request){
        //非空验证
        if(StringUtils.isEmptyOrWhitespace(username) || StringUtils.isEmptyOrWhitespace(password)) {
            return JSON.toJSONString(ResultData.fail("用户名或密码不能为空,请重试"));
        }

        //检验验证码输入
        String kaptchaCode = (String) request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
        if(!StringUtils.equalsIgnoreCase(verifyCode,kaptchaCode)){
            return JSON.toJSONString(ResultData.fail("验证码输入错误,请重试"));
        }

        List<User> userList = userService.searchAllUsers();
        //取出每一个user与输入进行对比
        for(User user : userList){
            if(user.getUsername().equals(username) && user.getPassword().equals(password)) {
                Cookie cookie = new Cookie("userId",user.getId()+"");
                cookie.setMaxAge(60 * 60 * 24 * 7);
                response.addCookie(cookie);
                return JSON.toJSONString(ResultData.success("登录成功",userService.searchAllUsers()));
            }
        }

        return JSON.toJSONString(ResultData.fail("用户名或密码错误,请重试"));
    }

4)目前的效果

登录界面
在这里插入图片描述
输入为空
在这里插入图片描述
登陆成功
在这里插入图片描述
用户列表
在这里插入图片描述
修改密码
在这里插入图片描述

5)加入Redis

步骤和kaptcha相似
1、引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2、配置类

//Redis配置类
@Configuration
@EnableCaching // 开启缓存支持
public class RedisConfig extends CachingConfigurerSupport {

    @Resource
    private LettuceConnectionFactory lettuceConnectionFactory;

    /**
     * RedisTemplate配置
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        // 设置序列化
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        RedisSerializer<?> stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);// key序列化
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
        redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 缓存配置管理器
     */
    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory factory) {
        // 配置序列化
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
        RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        // 以锁写入的方式创建RedisCacheWriter对象
        //RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
        // 创建默认缓存配置对象
        /* 默认配置,设置缓存有效期 1小时*/
        //RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
        /* 配置test的超时时间为120s*/
        RedisCacheManager cacheManager = RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter(factory)).cacheDefaults(redisCacheConfiguration)
                .withInitialCacheConfigurations(singletonMap("test", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(120)).disableCachingNullValues()))
                .transactionAware().build();
        return cacheManager;
    }

}

3、springboot配置

spring.redis.host=192.168.148.132//redis服务器IP
spring.redis.port=6379//redis服务器端口
spring.redis.database=0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0

4、修改Controller
登录模块

	@Autowired
    RedisTemplate redisTemplate;
    
	@PostMapping("/login")
    @ResponseBody
    public String login(@RequestParam("username") String username,
                         @RequestParam("password") String password,
                         @RequestParam("verifyCode") String verifyCode,
                         Model model,
                         HttpServletResponse response,
                         HttpServletRequest request){
        //省略部分格式审查代码

        //从Redis查
        ValueOperations opsForValue = redisTemplate.opsForValue();
        User user = JSON.parseObject((String)opsForValue.get(username),User.class);

        if(user == null || !user.getPassword().equals(password)){
            //如果从Redis找不到再从数据库找
            user = userService.searchUserByName(username);
            //还是找不到,返回结果
            if(user == null){
                return JSON.toJSONString(ResultData.fail("用户名或密码错误,请重试"));
            }else{
                //在数据库中找到,并更新到redis
                opsForValue.set(username,user);
                opsForValue.getAndExpire(username,7, TimeUnit.DAYS);//设置失效日期
                return JSON.toJSONString(ResultData.success("登录成功",userService.searchAllUsers()));
            }
        }else{
            opsForValue.getAndExpire(username,7, TimeUnit.DAYS);//更新失效日期
            return JSON.toJSONString(ResultData.success("登录成功",userService.searchAllUsers()));
        }
    }

注册模块

	@PostMapping("/register")
    @ResponseBody
    public String register(@RequestParam("username") String username,
                           @RequestParam("password") String password,
                           @RequestParam("verifyCode") String verifyCode,
                           Model model,
                           HttpServletResponse response,
                           HttpServletRequest request){
        //省略代码

        //插入数据库
        int res = userService.insert(user);
        //获取自增ID
        int id = userService.searchId();
        user.setId(id);
        //插入Redis(此部分为新增代码)
        ValueOperations opsForValue = redisTemplate.opsForValue();
        opsForValue.set(username,JSON.toJSONString(user));//数据结构:key:username value:user对象
        opsForValue.getAndExpire(username, 7,TimeUnit.DAYS);//7天过期
        
		//省略代码

    }

6)思考

以上已经完成了3个需求的实现,但是有些地方做的还不够好,如权限验证还没做、前端页面只做到水平集中,没有垂直居中等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值