(1)有状态登录概述

有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。

例如登录:用户登录后,我们把登录者的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session。然后下次请求,用户携带cookie值来,我们就能识别到对应session,从而找到用户的信息。

缺点是什么?

  • 服务端保存大量数据,增加服务端压力

  • 服务端保存用户状态,无法进行水平扩展

  • 客户端请求依赖服务端,多次请求必须访问同一台服务器

(2)无状态登录概述

微服务集群中的每个服务,对外提供的都是Rest风格的接口。而Rest风格的一个最重要的规范就是:服务的无状态性,即:

  • 服务端不保存任何客户端请求者信息

  • 客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份

带来的好处是什么呢?

  • 客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务

  • 服务端的集群和状态对客户端透明

  • 服务端可以任意的迁移和伸缩

  • 减小服务端存储压力

(3)实现无状态

无状态登录的流程:

  • 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)

  • 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证

  • 以后每次请求,客户端都携带认证的token

  • 服务的对token进行解密,判断是否有效。

流程图:

整个登录过程中,最关键的点是什么?

token的安全性

token是识别客户端身份的唯一标示,如果加密不够严密,被人伪造那就完蛋了。

采用何种方式加密才是安全可靠的呢?

我们将采用JWT + RSA非对称加密

(4)JWT

<1>简介

JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;官网:https://jwt.io

<2>数据格式

JWT包含三部分数据:

  • Header:头部,通常头部有两部分信息:

    • 声明类型,这里是JWT

    我们会对头部进行base64编码,得到第一部分数据

  • Payload:载荷,就是有效数据,一般包含下面信息:

    • 用户身份信息(注意,这里因为采用base64编码,可解码,因此不要存放敏感信息)

    • 注册声明:如token的签发时间,过期时间,签发人等

    这部分也会采用base64编码,得到第二部分数据

  • Signature:签名,是整个数据的认证信息。一般根据前两步的数据,再加上服务的的密钥(secret)(不要泄漏,最好周期性更换),通过加密算法生成。用于验证整个数据完整和可靠性

生成的数据格式:token==个人证件 jwt=个人身份证

可以看到分为3段,每段就是上面的一部分数据

<3>JWT交互流程

流程图:

步骤翻译:

  • 1、用户登录

  • 2、服务的认证,通过后根据secret生成token

  • 3、将生成的token返回给浏览器

  • 4、用户每次请求携带token

  • 5、服务端利用公钥解读jwt签名,判断签名有效后,从Payload中获取用户信息

  • 6、处理请求,返回响应结果

因为JWT签发的token中已经包含了用户的身份信息,并且每次请求都会携带,这样服务的就无需保存用户信息,甚至无需去数据库查询,完全符合了Rest的无状态规范。

<4>非对称加密

加密技术是对信息进行编码和解码的技术,编码是把原来可读信息(又称明文)译成代码形式(又称密文),其逆过程就是解码(解密),加密技术的要点是加密算法,加密算法可以分为三类:

  • 对称加密,如AES

    • 基本原理:将明文分成N个组,然后使用密钥对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。

    • 优势:算法公开、计算量小、加密速度快、加密效率高

    • 缺陷:双方都使用同样密钥,安全性得不到保证

  • 非对称加密,如RSA

    • 基本原理:同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端

      • 私钥加密,持有私钥或公钥才可以解密

      • 公钥加密,持有私钥才可解密

    • 优点:安全,难以破解

    • 缺点:算法比较耗时

  • 不可逆加密,如MD5,SHA

    • 基本原理:加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文。

RSA算法历史:

1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字缩写:RSA

<5>结合Zuul的鉴权流程

  • 我们首先利用RSA生成公钥和私钥。私钥保存在授权中心,公钥保存在Zuul和各个信任的微服务

  • 用户请求登录

  • 授权中心校验,通过后用私钥对JWT进行签名加密

  • 返回jwt给用户

  • 用户携带JWT访问

  • Zuul直接通过公钥解密JWT,进行验证,验证通过则放行

  • 请求到达微服务,微服务直接用公钥解析JWT,获取用户信息,无需访问授权中心

2.授权中心

(1)创建授权中心

授权中心的主要职责:

  • 用户鉴权:

    • 接收用户的登录请求,通过用户中心的接口进行校验,通过后生成JWT

    • 使用私钥生成JWT并返回

  • 服务鉴权:微服务间的调用不经过Zuul,会有风险,需要鉴权中心进行认证

    • 原理与用户鉴权类似,但逻辑稍微复杂一些(此处我们不做实现)

因为生成jwt,解析jwt这样的行为以后在其它微服务中也会用到,因此我们会抽取成工具。我们把鉴权中心进行聚合,一个工具module,一个提供服务的module

<1>创建父module

我们先创建父module(maven模块),名称为:leyou-auth

pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>leyou</artifactId>
<groupId>lucky.leyou.parent</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging> <groupId>lucky.leyou.auth</groupId>
<artifactId>leyou-auth</artifactId> </project>

注意:将pom打包方式改为pom

<2>通用module

然后是授权服务的通用模块:leyou-auth-common:

pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>leyou-auth</artifactId>
<groupId>lucky.leyou.auth</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion> <groupId>lucky.leyou.auth</groupId>
<artifactId>leyou-auth-common</artifactId> </project>

结构:

<3>授权服务

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>leyou-auth</artifactId>
<groupId>lucky.leyou.auth</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion> <groupId>lucky.leyou.auth</groupId>
<artifactId>leyou-auth-service</artifactId> <dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>lucky.leyou.auth</groupId>
<artifactId>leyou-auth-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>lucky.leyou.common</groupId>
<artifactId>leyou-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies> </project>

引导类:

package lucky.leyou;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LeyouAuthApplication { public static void main(String[] args) {
SpringApplication.run(LeyouAuthApplication.class, args);
}
}

application.yml:

server:
port: 8090
spring:
application:
name: auth-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
registry-fetch-interval-seconds: 10
instance:
lease-renewal-interval-in-seconds: 5 # 每隔5秒发送一次心跳
lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期

结构:

在leyou-gateway工程的application.yml中,修改路由:

zuul:
prefix: /api # 路由路径前缀
routes:
item-service: /item/** # 商品微服务的映射路径
search-service: /search/** # 搜索微服务
user-service: /user/** # 用户微服务
auth-service: /auth/** # 授权中心微服务

(2)JWT工具类

我们在leyou-auth-common中导入课前资料中的工具类:

需要在leyou-auth-common中引入JWT依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>leyou-auth</artifactId>
<groupId>lucky.leyou.auth</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion> <groupId>lucky.leyou.auth</groupId>
<artifactId>leyou-auth-common</artifactId> <dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
</dependencies> </project>

(3)测试工具类

我们在leyou-auth-common中编写测试类:

package lucky.leyou.auth.test;

import lucky.leyou.auth.entity.UserInfo;
import lucky.leyou.auth.utils.JwtUtils;
import lucky.leyou.auth.utils.RsaUtils;
import org.junit.Before;
import org.junit.Test; import java.security.PrivateKey;
import java.security.PublicKey; public class JwtTest { private static final String pubKeyPath = "C:\\tmp\\rsa\\rsa.pub"; private static final String priKeyPath = "C:\\tmp\\rsa\\rsa.pri"; private PublicKey publicKey; private PrivateKey privateKey; @Test
public void testRsa() throws Exception {
RsaUtils.generateKey(pubKeyPath, priKeyPath, "234");
} @Before
public void testGetRsa() throws Exception {
this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
this.privateKey = RsaUtils.getPrivateKey(priKeyPath);
} @Test
public void testGenerateToken() throws Exception {
// 生成token
String token = JwtUtils.generateToken(new UserInfo(20L, "jack"), privateKey, 5);
System.out.println("token = " + token);
} @Test
public void testParseToken() throws Exception {
String token = "eyJhbGciOiJSUzI1NiJ9.eyJpZCI6MjAsInVzZXJuYW1lIjoiamFjayIsImV4cCI6MTUzMzI4MjQ3N30.EPo35Vyg1IwZAtXvAx2TCWuOPnRwPclRNAM4ody5CHk8RF55wdfKKJxjeGh4H3zgruRed9mEOQzWy79iF1nGAnvbkraGlD6iM-9zDW8M1G9if4MX579Mv1x57lFewzEo-zKnPdFJgGlAPtNWDPv4iKvbKOk1-U7NUtRmMsF1Wcg"; // 解析token
UserInfo user = JwtUtils.getInfoFromToken(token, publicKey);
System.out.println("id: " + user.getId());
System.out.println("userName: " + user.getUsername());
}
}

<1>测试生成公钥和私钥

我们运行这段代码:注意需要把@Before方法注释掉

运行之后,查看目标目录:

公钥和私钥已经生成了!

<2>测试生成token

将@Before的注释去掉:

控制台输出:

<3>测试解析token:

正常情况:

任意改动token,发现报错了:

3.编写登录授权接口

接下来,我们需要在leyou-auth-servcice编写一个接口,对外提供登录授权服务。基本流程如下:

  • 客户端携带用户名和密码请求登录

  • 授权中心调用用户中心接口,根据用户名和密码查询用户信息

  • 如果用户名密码正确,能获取用户,否则为空,则登录失败

  • 如果校验成功,则生成JWT并返回

逻辑分析图:

(1)生成公钥和私钥

我们需要在授权中心生成真正的公钥和私钥。我们必须有一个生成公钥和私钥的secret,这个可以配置到leyou-auth-service模块的application.yml中:

leyou:
jwt:
secret: leyou@Login(Auth}*^31)&heiMa% # 登录校验的密钥
pubKeyPath: D:\temp\rsa\\rsa.pub # 公钥地址
priKeyPath: D:\temp\rsa\\rsa.pri # 私钥地址
expire: 30 # 过期时间,单位分钟

然后编写属性类,加载这些数据:

package lucky.leyou.auth.config;

import lucky.leyou.auth.utils.RsaUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey; @ConfigurationProperties(prefix = "leyou.jwt")
public class JwtProperties { private String secret; // 密钥 private String pubKeyPath;// 公钥路径 private String priKeyPath;// 私钥路径 private int expire;// token过期时间 private PublicKey publicKey; // 公钥 private PrivateKey privateKey; // 私钥 private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class); /**
* @PostContruct:在构造方法执行之后执行该方法
*/
@PostConstruct
public void init(){
try {
File pubKey = new File(pubKeyPath);
File priKey = new File(priKeyPath);
if (!pubKey.exists() || !priKey.exists()) {
// 生成公钥和私钥
RsaUtils.generateKey(pubKeyPath, priKeyPath, secret);
}
// 获取公钥和私钥
this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
this.privateKey = RsaUtils.getPrivateKey(priKeyPath);
} catch (Exception e) {
logger.error("初始化公钥和私钥失败!", e);
throw new RuntimeException();
}
} // getter setter ... public String getSecret() {
return secret;
} public void setSecret(String secret) {
this.secret = secret;
} public String getPubKeyPath() {
return pubKeyPath;
} public void setPubKeyPath(String pubKeyPath) {
this.pubKeyPath = pubKeyPath;
} public String getPriKeyPath() {
return priKeyPath;
} public void setPriKeyPath(String priKeyPath) {
this.priKeyPath = priKeyPath;
} public int getExpire() {
return expire;
} public void setExpire(int expire) {
this.expire = expire;
} public PublicKey getPublicKey() {
return publicKey;
} public void setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
} public PrivateKey getPrivateKey() {
return privateKey;
} public void setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
}
}

(2)controller

编写授权接口,我们接收用户名和密码,校验成功后,写入cookie中。

  • 请求方式:post

  • 请求路径:/accredit

  • 请求参数:username和password

  • 返回结果:无

代码:

这里的cookie的name和生存时间,我们配置到属性文件:application.yml:

leyou:
jwt:
secret: leyou@Login(Auth}*^31)&heiMa% # 登录校验的密钥
pubKeyPath: D:\temp\rsa\\rsa.pub # 公钥地址
priKeyPath: D:\temp\rsa\\rsa.pri # 私钥地址
expire: 30 # 过期时间,单位分钟
cookieName: LY_TOKEN #cookie的名称
cookieMaxAge: 30

然后在JwtProperties中添加属性,同时生成get和set方法:

代码:

package lucky.leyou.auth.controller;

import lucky.leyou.auth.config.JwtProperties;
import lucky.leyou.common.utils.CookieUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; @Controller
@EnableConfigurationProperties(JwtProperties.class)
public class AuthController { @Autowired
private AuthService authService; @Autowired
private JwtProperties prop; /**
* 登录授权
*
* @param username
* @param password
* @return
*/
@PostMapping("accredit")
public ResponseEntity<Void> authentication(
@RequestParam("username") String username,
@RequestParam("password") String password,
HttpServletRequest request,
HttpServletResponse response) {
// 登录校验
String token = this.authService.authentication(username, password);
if (StringUtils.isBlank(token)) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
// 将token写入cookie,并指定httpOnly为true,防止通过JS获取和修改
CookieUtils.setCookie(request, response, prop.getCookieName(),
token, prop.getCookieMaxAge(), null, true);
return ResponseEntity.ok().build();
}
}

注意:

这里我们使用了一个工具类,CookieUtils

(3)UserClient

接下来我们肯定要对用户密码进行校验,所以我们需要通过FeignClient去访问 user-service微服务

<1>在leyou-user-interface工程中添加api接口:

package lucky.leyou.user.api;

import lucky.leyou.user.domain.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam; public interface UserApi {
/**
* 根据用户名和密码查询用户
* @param username
* @param password
* @return
*/
@GetMapping("query")
public User queryUser(
@RequestParam("username") String username,
@RequestParam("password") String password
);
}

<2>在leyou-auth-service中编写FeignClient

在leyou-auth中引入user-service-interface依赖:

<dependency>
<groupId>lucky.leyou.user</groupId>
<artifactId>leyou-user-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

参照leyou-search或者leyou-goods-web

package lucky.leyou.auth.client;

import lucky.leyou.user.api.UserApi;
import org.springframework.cloud.openfeign.FeignClient; @FeignClient(value = "user-service")
public interface UserClient extends UserApi {
}

(4)AuthService

在leyou-auth-service:

package lucky.leyou.auth.service;

import lucky.leyou.auth.client.UserClient;
import lucky.leyou.auth.config.JwtProperties;
import lucky.leyou.auth.entity.UserInfo;
import lucky.leyou.auth.utils.JwtUtils;
import lucky.leyou.user.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; @Service
public class AuthService { @Autowired
private UserClient userClient; @Autowired
private JwtProperties properties; public String authentication(String username, String password) { try {
// 调用微服务,执行查询
User user = this.userClient.queryUser(username, password); // 如果查询结果为null,则直接返回null
if (user == null) {
return null;
} // 如果有查询结果,则生成token
String token = JwtUtils.generateToken(new UserInfo(user.getId(), user.getUsername()),
properties.getPrivateKey(), properties.getExpire());
return token;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

(5)项目完整结构

(6)测试

打开postman进行测试:http://localhost:8090/accredit

4.登录页面

接下来,我们看看登录页面,是否能够正确的发出请求。

我们在页面输入登录信息,然后点击登录:

查看控制台:

发现请求的路径不对,我们的认证接口是:

/api/auth/accredit

我们打开login.html,修改路径信息:

页面ajax请求:

然后再次测试,成功跳转到了首页:

5.解决cookie写入问题

接下来我们查看首页cookie:

cookie也是有 的限制,一个网页,只能操作当前域名下的cookie,但是现在我们看到的地址是0.0.1,而页面是www.leyou.com,域名不匹配,cookie设置肯定失败了!

(1)跟踪CookieUtils

我们去Debug跟踪CookieUtils,看看到底是怎么回事:

我们发现内部有一个方法,用来获取Domain:

我们发现内部有一个方法,用来获取Domain:

它获取domain是通过服务器的host来计算的,然而我们的地址竟然是:127.0.0.1:8087,因此后续的运算,最终得到的domain就变成了:

问题找到了:我们请求时的serverName明明是:api.leyou.com,现在却被变成了:127.0.0.1,因此计算domain是错误的,从而导致cookie设置失败!

(2)解决host地址的变化

这里的server name其实就是请求的时的主机名:Host,之所以改变,有两个原因:

  • 我们使用了nginx反向代理,当监听到api.leyou.com的时候,会自动将请求转发至127.0.0.1:10010,即Zuul。

  • 而后请求到达我们的网关Zuul,Zuul就会根据路径匹配,我们的请求是/api/auth,根据规则被转发到了 127.0.0.1:8087 ,即我们的授权中心。

我们首先去更改nginx配置,让它不要修改我们的host:proxy_set_header Host $host;

把nginx进行reload:

nginx -s reload

这样就解决了nginx这里的问题。但是Zuul还会有一次转发,所以要去修改网关的配置(leyou-gateway工程):

zuul:
prefix: /api # 路由路径前缀
routes:
item-service: /item/** # 商品微服务的映射路径
search-service: /search/** #搜索微服务
user-service: /user/** #用户微服务
auth-service: /auth/** # 授权中心微服务
add-host-header: true #携带请求本身的头信息

重启后,我们再次测试。

最终得到的domainName:

(3)Zuul的敏感头过滤

Zuul内部有默认的过滤器,会对请求和响应头信息进行重组,过滤掉敏感的头信息:

会发现,这里会通过一个属性为SensitiveHeaders的属性,来获取敏感头列表,然后添加到IgnoredHeaders中,这些头信息就会被忽略。

而这个SensitiveHeaders的默认值就包含了set-cookie

解决方案:

把敏感头设置为null

zuul:
prefix: /api # 路由路径前缀
routes:
item-service: /item/** # 商品微服务的映射路径
search-service: /search/** #搜索微服务
user-service: /user/** #用户微服务
auth-service: /auth/** # 授权中心微服务
add-host-header: true #携带请求本身的头信息
sensitive-headers: #覆盖默认敏感头信息

(4)测试

045 用户登录功能01----JWT和后台代码的更多相关文章

  1. Struts2整合Hibernate3实现用户登录功能

    所用技术:struts2 ,hibernate,jsp,mysql 本DEMO仅仅实现用户登录功能,采用MVC思想,自己也觉得相对是比较简单,比较容易理解数据流向的一个例子,通过整合这个过程,能够清晰 ...

  2. 实现Web上的用户登录功能

    关于如何实现web上的自动登录功能 文章来源http://coolshell.cn/articles/5353.html Web上的用户登录功能应该是最基本的功能了,可是在我看过一些站点的用户登录功能 ...

  3. 使用Struts1完成用户登录功能

    1.Struts框架 框架(framework):就是一系列代码和开发模式的整合,使用框架后,所有开发人员都会按照框架提供的规范进行开发,使代码更容易维护和扩展. 使用框架的优点: 1)   易于维护 ...

  4. 你会做Web上的用户登录功能吗?

    Web上的用户登录功能应该是最基本的功能了,可是在我看过一些站点的用户登录功能后,我觉得很有必要写一篇文章教大家怎么来做用户登录功能.下面的文章告诉大家这个功能可能并没有你所想像的那么简单,这是一个关 ...

  5. JavaWeb学习记录(六)——用户登录功能

    使用JDBC.spring框架.servlet实现一个简单的用户登录功能. 一.mySql数据库 SET FOREIGN_KEY_CHECKS=0; -- ---------------------- ...

  6. 利用MYSQL的函数实现用户登录功能,进出都是JSON(第二版)

    利用MYSQL的函数实现用户登录功能,进出都是JSON(第二版) CREATE DEFINER=`root`@`%` FUNCTION `uc_session_login`( `reqjson` JS ...

  7. 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(五)——实现注册功能

    使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(一)——创建应用 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(二)——使用蓝图功能进行模块化 使用 Flask 框架写用 ...

  8. 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(四)——对 run.py 的调整

    使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(一)——创建应用 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(二)——使用蓝图功能进行模块化 使用 Flask 框架写用 ...

  9. 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(一)——创建应用

    使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(一)——创建应用 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(二)——使用蓝图功能进行模块化 使用 Flask 框架写用 ...

随机推荐

  1. RESTful规范总结

    思维导图xmind文件:https://files-cdn.cnblogs.com/files/benjieming/RESTful%E8%A7%84%E8%8C%83.zip

  2. php超时时间说明【转】

    一,http请求超时时间 可能出现的场景: 1,curl进程运行了一个世纪还木结束,curl的时候设置了超时时间 --connect-timeout 1000 2,operation timed ou ...

  3. Vmware samba 搭建——Win10 共享

    配置环境 Win10 Vmware 15 ubuntu 16.04 Vmware 设置 安装 sudo apt-get install samba # 安装samba sudo apt-get ins ...

  4. 让configure和cmake编译时支持调试选项

    在Linux先编译软件基本都是采用configure文件生成makefile,或者,cmake生成makefile文件两种方式.它们生成的makefile文件,一般默认不支持-g调试选项.但我们使用这 ...

  5. 蓝色映象 幻舞少女之剑 BLUE REFLECTION 后感

    到底是看片收获多还是游戏收获多?在刷蓝色反射的时候刷了2部番.所以,我到底是为了什么在玩游戏呢? 岸田メル的人设,毋庸置疑,唯美想舔,且总能给人一种绝无杂质,纯洁治愈的感觉,再加上浅野隼人的配乐,恰如 ...

  6. centos7静黙安装Oracle11.2.0软件响应文件oracle_install.rsp

    oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_v11_2_0 oracle.i ...

  7. 不要让事实妨碍好故事:Facebook精准广告产品与硅谷创业揭秘,4星奇书《混乱的猴子》

        “ 现在,和往常一样,一些有先见之明的学者看到了这一天会再次到来,他们把这次全新的媒体中世纪化过程命名为“第二次口口相传”(Secondary Orality)和“古登堡右括号”(the Gu ...

  8. I2C协议简介

    主从芯片如何传输数据 AT24C02是一个存储芯片,需要把数据从ARM板发给AT24C02,也需要从AT24C02读取数据. I2C是一个主从结构,Master发起传输,slave接收或回应 一主多从 ...

  9. Docker Compose YML文件配置

    Docker Compose YML 默认的模板文件是 docker-compose.yml,其中定义的每个服务都必须通过 image 指令指定镜像或 build 指令(需要 Dockerfile)来 ...

  10. Java反射之Bean修改更新属性值等工具类

    package com.bocean.util; import java.lang.annotation.Annotation; import java.lang.reflect.Field; imp ...