Asp.Net.Identity认证不依赖Entity Framework实现方式
Asp.Net.Identity为何物请自行搜索,也可转向此文章http://www.cnblogs.com/shanyou/p/3918178.html
本来微软已经帮我们将授权、认证以及数据库存储都一一处理好了。但是总有这种情况,如我们现在的项目是已经存在了数据库,且库里已经有用户、角色等信息表,但是
我们还是贪心想使用微软的授权、认证类库。这里我就来实际实践下到底可行不可行~
第一步、新建一个Asp.Net MVC框架的web工程
第二部、Nuget上安装Microsoft.AspNet.Identity、Microsoft.AspNet.Identity.Owin
其中Microsoft.AspNet.Identity.Owin有依赖项,它依赖了这几个包:
Microsoft.Owin.Security.OAuth MSDN注解:包含与 OAuth 提供程序相关的类型。(详细信息参考 https://msdn.microsoft.com/zh-cn/library/microsoft.owin.security.oauth(v=vs.111).aspx)
Microsoft.Owin.Security.Cookies MSDN注解:提供与身份 cookie 相关的类型。 (详细信息参考 https://msdn.microsoft.com/zh-cn/library/microsoft.owin.security.cookies(v=vs.111).aspx)
Microsoft.Owin.Security MSDN注解:包含与身份验证相关的类型。 (详细信息参考 https://msdn.microsoft.com/zh-cn/library/microsoft.owin.security(v=vs.111).aspx)
Microsoft.AspNet.Identity.Core MSDN注解:包含与管理 ASP.NET Identity 的用户和角色相关的类和接口。
(信息信息参考:https://msdn.microsoft.com/library/microsoft.aspnet.identity(v=vs.111).aspx)
从MSDN的注解可以看出来Microsoft.AspNet.Identity.Owin里其实就是将网站的登录、注册业务场景所需的API进行了封装;
第三部、建模
如我现在的数据库的用户表为BASE_USER,表结构如下
CREATE TABLE [dbo].[BASE_USER](
[ID] [uniqueidentifier] NOT NULL PRIMARY KEY,
[NAME] [varchar](50) NOT NULL,
[PWD] [varchar](50) NOT NULL,
) ON [PRIMARY]
我们在工程站点的Models文件夹里新建一个BASE_USER类,让它继承Microsoft.AspNet.Identity.IUser<GUID>,这里我们加一个数据表不存在的NICKNAME昵称字段,到后面看看会有什么效果~
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace IdeintityDemo.Models
{
public class BASE_USER : Microsoft.AspNet.Identity.IUser<Guid>
{
/// <summary>
/// 用户编号
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 密码
/// </summary>
public string PWD { get; set; }
/// <summary>
/// 昵称
/// </summary>
public string NickName { get; set; } public bool RememberMe { get; set; }
}
}
第四部 创建UserStore类,该类通过继承接口IUserStore来实现用户存储在数据库的api
using Microsoft.AspNet.Identity;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Security.Claims;
using IdeintityDemo.Models;
using IdeintityDemo.Common; namespace IdeintityDemo.Identity
{ /// <summary>
/// 用户持久化存储对象
/// 必须实现Microsoft.AspNet.Identity相应接口,否则在SignInManager类进行登录校验过程中
/// 会弹出未实现相关接口的异常!
/// IUserStore:检测是否存在账户
/// IUserPasswordStore:校验密码
/// IUserLockoutStore:锁定账户相关操作
/// IUserClaimStore:存储用户特定的声明
/// IUserEmailStore:邮箱关联、验证等相关操作
/// IUserPhoneNumberStore:手机关联、验证相关操作
/// IUserTwoFactorStore:获取或设置用户双重身份验证的方法。
/// </summary>
public class HsUserStore : Microsoft.AspNet.Identity.IUserStore<BASE_USER, Guid>,
Microsoft.AspNet.Identity.IUserPasswordStore<BASE_USER, Guid>,
Microsoft.AspNet.Identity.IUserLockoutStore<BASE_USER, Guid>,
Microsoft.AspNet.Identity.IUserClaimStore<BASE_USER, Guid>,
Microsoft.AspNet.Identity.IUserEmailStore<BASE_USER, Guid>,
Microsoft.AspNet.Identity.IUserPhoneNumberStore<BASE_USER, Guid>,
Microsoft.AspNet.Identity.IUserTwoFactorStore<BASE_USER, Guid>
{ /// <summary>
/// 声明
/// </summary>
public IList<System.Security.Claims.Claim> Claims = null;
/// <summary>
/// 用户
/// </summary>
public BASE_USER UserIdentity = null; /// <summary>
/// 实例化
/// </summary>
public HsUserStore()
{
//声明
Claims = new List<System.Security.Claims.Claim>();
}
/// <summary>
/// 创建用户
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task CreateAsync(BASE_USER user)
{
return Task.Run(() => {
string sql = @"INSERT INTO [dbo].[BASE_USER]([ID],[NAME],[PWD])
VALUES(@UserID,@name,@pwd)";
SqlParameter[] parameters = {
new SqlParameter("@UserID", Guid.NewGuid()),
new SqlParameter("@name", user.UserName),
new SqlParameter("@pwd", user.PWD)
};
int iResult = DbHelperSQL.ExecuteSql(sql, parameters);
});
}
/// <summary>
/// 删除用户
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task DeleteAsync(BASE_USER user)
{
return Task.Run(() => {
string sql = @"DELETE FROM [dbo].[BASE_USER] WHERE ID=@ID";
SqlParameter[] parameters = {
new SqlParameter("@UserID", user.Id)};
int iResult = DbHelperSQL.ExecuteSql(sql, parameters);
});
}
/// <summary>
/// 根据用户id获取用户
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public Task<BASE_USER> FindByIdAsync(Guid userId)
{
return Task<BASE_USER>.Run(() =>
{
BASE_USER result = new BASE_USER();
string sql = @"SELECT * FROM [dbo].[BASE_USER] WHERE ID=@ID";
SqlParameter[] parameters = {
new SqlParameter("@ID", userId)};
DataSet ds = DbHelperSQL.Query(sql, parameters);
if (ds == null || ds.Tables == null || ds.Tables[].Rows.Count <= )
return result;
//model
DataRow dr = ds.Tables[].Rows[];
result.Id = Guid.Parse(dr["ID"].ToString());
result.UserName = dr["NAME"].ToString();
result.PWD = dr["PWD"].ToString();
return result;
});
}
/// <summary>
/// 根据名称获取用户信息
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
public Task<BASE_USER> FindByNameAsync(string userName)
{
return Task<BASE_USER>.Run(() =>
{
BASE_USER result = new BASE_USER();
string sql = @"SELECT * FROM [dbo].[BASE_USER] WHERE NAME=@NAME";
SqlParameter[] parameters = {
new SqlParameter("@NAME", userName)};
DataSet ds = DbHelperSQL.Query(sql, parameters);
if (ds == null || ds.Tables == null || ds.Tables[].Rows.Count <= )
return result;
//model
DataRow dr = ds.Tables[].Rows[];
result.Id = Guid.Parse(dr["ID"].ToString());
result.UserName = dr["NAME"].ToString();
result.PWD = dr["PWD"].ToString(); return result;
});
}
/// <summary>
/// 更新用户
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task UpdateAsync(BASE_USER user)
{
return Task.Run(() =>
{
//省略...
});
}
/// <summary>
/// 异步返回当前失败的访问尝试次数。当密码被验证或帐户被锁定时,这个数字通常会被重置。
/// (这里因为我数据库里没有去做这一块的记录保存,所以先写死返回1)
/// </summary>
/// <param name="user">用户</param>
/// <returns></returns>
public Task<int> GetAccessFailedCountAsync(BASE_USER user)
{
return Task.FromResult<int>();
}
/// <summary>
/// 获取锁定状态
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<bool> GetLockoutEnabledAsync(BASE_USER user)
{
return Task.FromResult<bool>(false);
}
/// <summary>
/// 获取锁定结束时间
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<DateTimeOffset> GetLockoutEndDateAsync(BASE_USER user)
{
throw new NotImplementedException();
}
/// <summary>
/// 记录试图访问用户失败的记录。
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<int> IncrementAccessFailedCountAsync(BASE_USER user)
{
return Task.FromResult<int>();
}
/// <summary>
/// 重置访问失败计数,通常在帐户成功访问之后
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task ResetAccessFailedCountAsync(BASE_USER user)
{
return Task.FromResult(false);
}
/// <summary>
/// 异步设置是否可以锁定用户。
/// </summary>
/// <param name="user"></param>
/// <param name="enabled"></param>
/// <returns></returns>
public Task SetLockoutEnabledAsync(BASE_USER user, bool enabled)
{
return Task.Run(() => { });
}
/// <summary>
/// 异步锁定用户直到指定的结束日期
/// </summary>
/// <param name="user"></param>
/// <param name="lockoutEnd"></param>
/// <returns></returns>
public Task SetLockoutEndDateAsync(BASE_USER user, DateTimeOffset lockoutEnd)
{
return Task.Run(() =>
{ });
}
/// <summary>
/// 获取用户密码
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<string> GetPasswordHashAsync(BASE_USER user)
{
return Task<string>.Run(() =>
{
return user.PWD;
});
}
/// <summary>
/// 是否有密码
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<bool> HasPasswordAsync(BASE_USER user)
{
return Task.FromResult<bool>(!string.IsNullOrEmpty(user.PWD));
}
/// <summary>
/// 密码进行加密
/// </summary>
/// <param name="user"></param>
/// <param name="passwordHash"></param>
/// <returns></returns>
public Task SetPasswordHashAsync(BASE_USER user, string passwordHash)
{
return Task.Run(() =>
{
user.PWD = passwordHash;//加密后
});
}
/// <summary>
/// 添加一个声明
/// </summary>
/// <param name="user"></param>
/// <param name="claim"></param>
/// <returns></returns>
public Task AddClaimAsync(BASE_USER user, Claim claim)
{
return Task.Run(() => { Claims.Add(claim); });
}
/// <summary>
/// 获取改用户的所有声明
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<IList<Claim>> GetClaimsAsync(BASE_USER user)
{
return Task.Run<IList<System.Security.Claims.Claim>>(() =>
{
IList<System.Security.Claims.Claim> list = new List<System.Security.Claims.Claim>();
return list;
});
}
/// <summary>
/// 移除申明
/// </summary>
/// <param name="user"></param>
/// <param name="claim"></param>
/// <returns></returns>
public Task RemoveClaimAsync(BASE_USER user, Claim claim)
{
return Task.Run(() =>
{ });
}
/// <summary>
/// 通过邮箱获取对应的用户信息
/// </summary>
/// <param name="email"></param>
/// <returns></returns>
public Task<BASE_USER> FindByEmailAsync(string email)
{
return Task<BASE_USER>.Run(() => new BASE_USER());
}
/// <summary>
/// 获取邮箱
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<string> GetEmailAsync(BASE_USER user)
{
return Task<string>.Run(() => string.Empty);
}
/// <summary>
/// 确认邮箱
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<bool> GetEmailConfirmedAsync(BASE_USER user)
{
return Task.FromResult<bool>(true);
}
/// <summary>
/// 修改邮箱
/// </summary>
/// <param name="user"></param>
/// <param name="email"></param>
/// <returns></returns>
public Task SetEmailAsync(BASE_USER user, string email)
{
return Task.Run(() => { });
}
/// <summary>
///设置用户是否邮箱确认
/// </summary>
/// <param name="user"></param>
/// <param name="confirmed"></param>
/// <returns></returns>
public Task SetEmailConfirmedAsync(BASE_USER user, bool confirmed)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取联系电话
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<string> GetPhoneNumberAsync(BASE_USER user)
{
return Task.FromResult<string>(string.Empty);
}
/// <summary>
/// 获取用户电话号码是否已确认
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<bool> GetPhoneNumberConfirmedAsync(BASE_USER user)
{
return Task.FromResult<bool>(true);
}
/// <summary>
/// 设置用户电话号码
/// </summary>
/// <param name="user"></param>
/// <param name="phoneNumber"></param>
/// <returns></returns>
public Task SetPhoneNumberAsync(BASE_USER user, string phoneNumber)
{
return Task.Run(() => { });
}
/// <summary>
/// 设置与用户关联的电话号码
/// </summary>
/// <param name="user"></param>
/// <param name="confirmed"></param>
/// <returns></returns>
public Task SetPhoneNumberConfirmedAsync(BASE_USER user, bool confirmed)
{
return Task.Run(() => { });
}
/// <summary>
/// 是否为用户启用了双重身份验证。
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public Task<bool> GetTwoFactorEnabledAsync(BASE_USER user)
{
return Task.FromResult<bool>(false);
}
/// <summary>
/// 设置双重身份验证
/// </summary>
/// <param name="user"></param>
/// <param name="enabled"></param>
/// <returns></returns>
public Task SetTwoFactorEnabledAsync(BASE_USER user, bool enabled)
{
throw new NotImplementedException();
}
/// <summary>
/// 释放
/// </summary>
public void Dispose()
{
throw new NotImplementedException();
} }
}
第五步继承UserManager类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using IdeintityDemo.Models;
using Microsoft.AspNet.Identity; namespace IdeintityDemo.Identity
{
public class HsUserManager:UserManager<BASE_USER,Guid>
{
/// <summary>
/// 通过构造函数注入用户存储实现类
/// </summary>
/// <param name="store"></param>
public HsUserManager(HsUserStore store) : base(store)
{ }
}
}
上面的代码特别特别简单,但是确很重要,你就理解为将上面定义的HsUserStore注入到一个IOC容器里就好了。 如果不注入,你对用户的所有读写db操作都没有,后续整个登录、注册、验证业务都无法实施!
第六步继承SignInManager 类
SignInManager类是Microsoft.AspNet.Identity.Owin命名空间下的,集成了用户登录进行管理的相关API,是Asp.Net.Identity里必不可少的处理类
我们需要自定义一个登录管理类,继承SignInManager。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using IdeintityDemo.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using System.Threading.Tasks;
using Microsoft.Owin.Security; namespace IdeintityDemo.Identity
{
/// <summary>
/// 登录管理,此处用到了UserManager
/// </summary>
public class HsSignInManager : SignInManager<BASE_USER, Guid>
{ /// <summary>
/// 构造函数
/// </summary>
/// <param name="UserManager"></param>
/// <param name="AuthenticationManager"></param>
public HsSignInManager(Microsoft.AspNet.Identity.UserManager<BASE_USER, Guid> UserManager, Microsoft.Owin.Security.IAuthenticationManager AuthenticationManager)
: base(UserManager, AuthenticationManager)
{ } /// <summary>
/// 根据用户名密码,验证用户登录
/// </summary>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <param name="isPersistent"></param>
/// <param name="shouldLockout"></param>
/// <returns></returns>
public override System.Threading.Tasks.Task<Microsoft.AspNet.Identity.Owin.SignInStatus> PasswordSignInAsync(string userName,
string password,
bool isPersistent,
bool shouldLockout)
{
/*这里可以直接通过PasswordSignInAsync来校验,也可以重写~ */
//这里用Find方法会返回空的user。。。搞不懂。。
var user = base.UserManager.FindByName<BASE_USER, Guid>(userName);
if (user == null || user.Id == Guid.Empty)
return Task.FromResult<SignInStatus>(SignInStatus.Failure);
else if (user.PWD != password)
return Task.FromResult<SignInStatus>(SignInStatus.Failure);
else
{
/*这个时候如果不写入到cooks里,在Home控制器的Index action里会被系统的
Authorize刷选器拦截*/
// 利用ASP.NET Identity获取identity 对象
var identity = base.UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// 将上面拿到的identity对象登录
base.AuthenticationManager.SignIn(new AuthenticationProperties()
{ IsPersistent = true }, identity.Result);
return Task.FromResult<SignInStatus>(SignInStatus.Success);
}
/*这里如果你想直接使用微软的登入方法也可以,直接base.就ok啦*/
//return base.PasswordSignInAsync(userName,
// password,
// isPersistent,
// shouldLockout);
} }
}
上面最重要的方法就是PasswordSignInAsync,这个方法就是登入方法。
可以说我们封装的工作已经完成了,封装了三个类

现在我们看Controller里的代码是怎么写的吧~
这是注册的Action,所属Controller当然是AccountController啦。。
/// <summary>
/// 注册
/// </summary>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Register(BASE_USER user)
{
Microsoft.Owin.IOwinContext OwinContext = HttpContext.GetOwinContext();
//用户储存
HsUserStore userStore = new HsUserStore();
//UserManager
HsUserManager UserManager = new HsUserManager(userStore);
IdentityResult result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
Response.Write("注册成功!");
return RedirectToAction("index", "home");
}
return View();
} [AllowAnonymous]
public ActionResult Register()
{
return View();
}
接下来是登录的Action代码~
[AllowAnonymous]
public ActionResult Login()
{
return View();
} [HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Login(BASE_USER user)
{
if (string.IsNullOrEmpty(user.UserName)) { return View(); }
if (string.IsNullOrEmpty(user.PWD)) { return View(); }
//Context
Microsoft.Owin.IOwinContext OwinContext = HttpContext.GetOwinContext();
//实例化UserStore对象
HsUserStore userStore = new HsUserStore();
//UserManager
HsUserManager userManager = new HsUserManager(userStore);
//授权对象
IAuthenticationManager autherticationManager = OwinContext.Authentication;
//登录管理对象
HsSignInManager signManager = new HsSignInManager(userManager, autherticationManager); //登录
Microsoft.AspNet.Identity.Owin.SignInStatus SignInStatus = Microsoft.AspNet.Identity.Owin.SignInStatus.Failure;
try
{
SignInStatus = await signManager.PasswordSignInAsync(user.UserName,
user.PWD,
true,
shouldLockout: false); }catch(Exception ea)
{ }
//登录状态
switch (SignInStatus)
{
//成功 同一个Control里跳转直接使用RecirectToAction(ActionName)
case Microsoft.AspNet.Identity.Owin.SignInStatus.Success:
//不同控制器使用RedirectToAction
return RedirectToAction("Index", "Home"); //可以直接跳到别的Controller.
//锁定
case Microsoft.AspNet.Identity.Owin.SignInStatus.LockedOut:
Response.Write("账户被锁定啦~~~!");
break;
//失败
case Microsoft.AspNet.Identity.Owin.SignInStatus.Failure:
Response.Write("登录失败啦~~~!");
break;
//要求验证
case Microsoft.AspNet.Identity.Owin.SignInStatus.RequiresVerification:
Response.Write("需要验证!");
break; }
return View();
}
我们看我们Mvc路由默认配置初始页面是Home控制器下的Index

这是Home控制器下的Index声明

F5运行试下
运行发现浏览器直接跳转到了登录页面。。

在我们输入账号密码后,跳转到了Index页面

然后我们用浏览器查看下cookie有什么变化~

发现整个业务完成后浏览器保存了名为_IdentityDemo的 一个存储项,整个就是在我们登入方法里执行的。
好啦,整个Identity认证不依赖EF已经实现了,几个核心点就是需要实现IUser接口以及各种Store。。然后将实现各种Store的类注入到UserManager构造函数里
等有时间再实现下Identity命名空间下的角色管理这一块吧。。。不得不说微软的接口真是封装的好啊~
这里感谢下开源中国的李朝强,我是看他博客再自己实践出来的~感谢感谢~
u
Asp.Net.Identity认证不依赖Entity Framework实现方式的更多相关文章
- Asp.Net MVC4开发二: Entity Framework在Asp.Net MVC4中的应用
ORM作为一种数据库訪问机制已广泛地应用于各种项目其中,在.Net开发中,应用比較广泛的ORM框架大致有以下几个: 官方支持的有:Linq to SQL.Entity Framework.三方的有:N ...
- ASP.NET Core 1.0: Using Entity Framework Core
伴随着ASP.NET Core 1.0发布的还有Entity Framework Core 1.0; 官方文档链接:https://docs.efproject.net/en/latest/platf ...
- 2、ASP.NET MVC入门到精通——Entity Framework入门
实体框架(Entity Framework)简介 简称EF 与ADO.NET关系 ADO.NET Entity Framework 是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R ...
- ASP.NET没有魔法——ASP.NET MVC 与数据库之Entity Framework Migrations
在开发数据库应用程序的时候,经常会遇到某些表需要添加字段或者修改类型.新增表等需求,而对于EF Code First来说关注的只有实体类,当需求变更时只需要添加新的实体类或者在实体类中添加.删除.修改 ...
- [转]ASP.NET Core 1.0: Using Entity Framework Core 1.0 - Transaction
本文转自:http://blog.csdn.net/alvachien/article/details/51576961 跟Entity Framework之前的版本不同,Class DbContex ...
- ASP.NET Core 1.0: Using Entity Framework Core 1.0 - Transaction
跟Entity Framework之前的版本不同,Class DbContext不再有AcceptAllChanges()方法. 使用Transaction需要使用DbContext中的Databas ...
- 3、ASP.NET MVC入门到精通——Entity Framework增删改查
这里我接上讲Entity Framework入门.从网上下载Northwind数据库,新建一个控制台程序,然后重新添加一个ado.net实体数据模型. EF中操作数据库的"网关"( ...
- ASP.NET开发实战——(十二)ASP.NET MVC 与数据库之Entity Framework Migrations
在开发数据库应用程序的时候,经常会遇到某些表需要添加字段或者修改类型.新增表等需求,而对于EF Code First来说关注的只有实体类,当需求变更时只需要添加新的实体类或者在实体类中添加.删除.修改 ...
- 【ASP.NET Identity系列教程(三)】Identity高级技术
注:本文是[ASP.NET Identity系列教程]的第三篇.本系列教程详细.完整.深入地介绍了微软的ASP.NET Identity技术,描述了如何运用ASP.NET Identity实现应用程序 ...
随机推荐
- 【强连通分量+spfa】Bzoj1179 Apio2009 Atm
Description Solution 显然缩强连通分量,然后求最长路,虽然是DAG但还是有点麻烦,于是用了spfa. Code 重建图_数组写错好多次,感觉做这题也就是练了一下实现. #inclu ...
- BZOJ_2679_[Usaco2012 Open]Balanced Cow Subsets _meet in middle+双指针
BZOJ_2679_[Usaco2012 Open]Balanced Cow Subsets _meet in middle+双指针 Description Farmer John's owns N ...
- 这么用Mac才叫爽!
用了近一年的 Macbook Pro,已经离不开它了.真是生活工作学习必备之良品啊. 如果你将要买苹果电脑或者刚买,那么不妨看看此文.推荐一些个人觉得好用的软件,而Mac本身的使用技巧----触控板. ...
- HTML标题
HTML 标题 在 HTML 文档中,标题很重要. HTML 标题 标题(Heading)是通过 <h1> - <h6> 标签进行定义的. <h1> 定义最大的标题 ...
- 已管理员身份从cmd框进入mysql,及常用的简单操作!
在命令框中操作mysql已管理员的身份进入操作权限较高,已普通用户进入cmd框也可对mysql进行操作,不过一般建议用管理员身份进入. 1.启动MYSQL Notifier 2.已管理员身份进入cmd ...
- JDK--box和unbox
目录 什么是装箱.拆箱 基本类型和包装类型 为什么会有基本类型? 为什么还要有包装类型 两者区别 两者互转 源码分析(JDK1.8版本) valueOf方法 1.Integer.Short.Byte. ...
- 从壹开始 [vueAdmin后台] 之三 || 动态路由配置 & 项目快速开发
回顾 今天VS 2019正式发布,实验一波,你安装了么?Blog.Core 预计今天会升级到 Core 3.0 版本. 哈喽大家周三好!本来今天呢要写 Id4 了,但是写到了一半,突然有人问到了关于 ...
- 我眼中的 Nginx(四):是什么让你的 Nginx 服务退出这么慢?
张超:又拍云系统开发高级工程师,负责又拍云 CDN 平台相关组件的更新及维护.Github ID: tokers,活跃于 OpenResty 社区和 Nginx 邮件列表等开源社区,专注于服务端技术的 ...
- 使用BeautifulSoup和正则表达式爬取时光网不同地区top100电影并使用Matplotlib对比
还有一年多就要毕业了,不准备考研的我要着手准备找实习及工作了,所以一直没有更新. 因为Python是自学不久,发现很久不用的话以前学过的很多方法就忘了,今天打算使用简单的BeautifulSoup和一 ...
- openlayers4 入门开发系列之小区信号扇形图篇
前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...