java初探之登录终探
Posted lovejune
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java初探之登录终探相关的知识,希望对你有一定的参考价值。
上一章讲了表单验证,数据验证和加密。这一章,将研究服务器和数据库的交互过程。
后端服务器有两种主流的形式,SQL数据库和NOSQL数据库。其中mysql属于SQL数据库,REDIS属于非SQL数据库。先介绍SQL数据库与服务器的交互过程。
-
建立数据库表
CREATE TABLE `user_test` ( `id` BIGINT(20) NOT NULL COMMENT \'用户ID,手机号码\', `password` VARCHAR(32) DEFAULT NULL COMMENT \'MD5(MD5(pass明文+固定salt) + salt)\', PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
因为是测试,就使用两个字段的用户表。
-
在配置文件中设置数据库的连接 application.properties文件
#DB Configuration: spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/miaosha?useUnicode=true&characterEncoding=utf8 spring.datasource.username=root spring.datasource.password=root
springboot号称一键构建,只需要在配置文件里配置了数据库的连接,不需配置任何bean和类,即可完成数据库的操作,即基于注解的配置,当然数据库的CURD操作也可以在xml的MAPPER里完成,但注解方式更为简单,本项目只采用注解的方式。
-
在dao包中编写UserMapper类,完成简单的查询操作
@Repository
@Mapper
public interface UserMapper {
//根据id查询用户
@Select("select * from user where id = #{mobile}")
@Results(id = "userMap" ,value = {
@Result(id = true,column = "id",property = "mobile"),
@Result(column = "password",property = "password")}
)
public Person userById(String mobile);
}
-
改写UserService类,完成从数据库查询到登录的操作
//3.根据用户手机查询用户 public Person personByMobile(String mobile){ return userMapper.userById(mobile); }
到目前为止,已经完成了从数据库到服务器的交互过程。但是如果每次查询操作到要与数据库连接,由于延迟,会造成很大的阻塞,因此采用REDIS技术,将数据缓存到内存中,每次查询,只要查缓存中的数据即可,会快很多。
下面研究REDIS技术
使用自带的redis模板,不需要创建redis的配置文件,只需要在application.properties中配置
-
配置application.properties
#Redis Configuration:
spring.redis.host=localhost
spring.redis.port=6379
-
需要导入的坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
导入坐标之后,可以对RedisTemplate直接注入,并使用其方法,但RedisTemplate的注入需要注意几点:
1.当private RedisTemplate<String ,String > redisTemplate;时,即key与value都是基础类型时,注入只需要@Autowired即可成功。
但当value值是自己定义的类时,注入需要先将该类序列化,即继承序列化接口,然后使用@Resource注解进行注入。即可成功。
-
重写UserService类
public Result login(Person person){
if(person==null){
throw new GobalException(0,"该用户为空");
}
String mobile=person.getMobile();
String password=person.getPassword();
//根据id得到数据库用户
Person persondb = personByMobile(mobile);
if(persondb==null){
throw new GobalException(0,"查无此人");
}
String dbPassword = persondb.getPassword();
String formPass = Md5Util.dbPass2FormPass(dbPassword);
if(!formPass.equals(password)){
throw new GobalException(0,"密码错误");
}
return Result.SUCCESS;
}
//3.根据用户手机查询用户
public Person personByMobile(String mobile){
Person person = redisTemplate.boundValueOps(mobile).get();
if(person!=null){
return person;
}
person=userMapper.userById(mobile);
if(person!=null){
redisTemplate.boundValueOps(mobile).set(person);
}
return person;
}
-
测试
两次查询,第一次从数据库查询,以后就从缓存上查询。
以上我们的登陆场景的代码差不多分析完了,但为了扩展性,对redis的key值进行分析,在网站的未来,不止有用户,还有商品等各种,对key值得管理,不能随意的取名造成混乱,因此,我们需要抽象出一个专门key的类来。
-
创建一个key的接口
public interface KeyPrefix {
int expireSeconds();//过期时间
String getPrefix();//key值
}
-
抽象一个key的基本抽象类
public abstract class BasePrefix implements KeyPrefix{
private int expireSeconds;
private String prefix;
public BasePrefix(String prefix){
this(0,prefix);
}
public BasePrefix(int expireSeconds,String prefix){
this.expireSeconds=expireSeconds;
this.prefix=prefix;
}
public int expireSeconds() {
return expireSeconds;
}
public String getPrefix() {
String className=getClass().getSimpleName();
return className+":"+prefix;
}
}
调用getPrefix()即可得到类加上前缀的key值。
-
创建UserKey值
public class UserKey extends BasePrefix { public static final int TOKEN_EXPIRE=3600*24*2; public UserKey(int expireSeconds,String prefix) { super(expireSeconds,prefix); } public static UserKey token=new UserKey(TOKEN_EXPIRE,"tk"); public static UserKey getById=new UserKey(0,"id"); }
至此,我们创建了一个Key值得抽象功能,如果有商品的数据需要存入Redis,则可以再创建一个GoodKey类即可。
接下来,需要创建一个redis的service类,来对数据进行一定的操作,该服务类需要有复用功能,因此用到泛型的概念,我们要通过key值取到自己想得到的类,那么不能直接存储对象本身,而是将对象序列化为一个字符串(通常是一个json字符串),然后存入redis中,取出后,再还原对象。
-
创建RedisService
@Service public class RedisService { @Autowired private RedisTemplate<String, String> redisTemplate; //1.把字符串转换为bean对象 public static <T> T stringToBean(String str, Class<T> clazz) { ObjectMapper objectMapper = new ObjectMapper(); T t = null; if(str==null || str.length()==0){ return t; } try { t = objectMapper.readValue(str, clazz); } catch (Exception e) { throw new GobalException(0,"REDIS字符串转换异常"); } return t; } //2.将bean对象转换为string public static <T> String beanToString(T value) { ObjectMapper objectMapper = new ObjectMapper(); String json = null; if(value==null){ return json; } try { json = objectMapper.writeValueAsString(value); } catch (JsonProcessingException e) { throw new GobalException(0,"REDIS字符串转换异常"); } return json; } //3.获取对象 public <T> T get(KeyPrefix prefix, String key, Class<T> clazz){ //真正的key String realKey=prefix.getPrefix()+key; String str = redisTemplate.opsForValue().get(realKey); T t = stringToBean(str, clazz); return t; } //4.设置对象 public <T> void set(KeyPrefix prefix, String key, T value) { String str = beanToString(value); String realKey=prefix.getPrefix()+key; redisTemplate.opsForValue().set(realKey,str); } //5.判断key值是否存在 public <T> boolean exists(KeyPrefix prefix, String key) { String realKey = prefix.getPrefix() + key; return redisTemplate.hasKey(realKey); } //6.增加值 public <T> Long incr(KeyPrefix prefix, String key) { String realKey = prefix.getPrefix() + key; return redisTemplate.opsForValue().increment(realKey, 1); } //7.减少值 public <T> Long decr(KeyPrefix prefix, String key) { String realKey = prefix.getPrefix() + key; return redisTemplate.opsForValue().increment(realKey, -1); } //8.扫描key值 // public List<String> scanKeys(String key) { // // } //9.删除key值 public void delete(KeyPrefix prefix, String key) { String realKey=prefix.getPrefix()+key; redisTemplate.delete(realKey); } }
这里应用了泛型的思想,可以保存任何类型的值,思想是,利用JSON类,将某个实体类转换为JSON字符串,然后将字符串存到REDIS中。
到此为止,登录场景所用到的技术和编码工作全部完成,总结如下;
- 抽象:将某些属性抽象出来,可以增加复用性,降低耦合,比如Key类。
- 安全:登录时的密码需要加密,然后在网路中传输。
- 整合:使用框架提供的工具对MVC进行整合,最终完成项目。
下一章将对登录场景的编码进行进一步改进,包括在页面的验证,返回消息的抽象,以及使用Jedis的原生方法完成Redis的缓存操作。
以上是关于java初探之登录终探的主要内容,如果未能解决你的问题,请参考以下文章