0%

1. 下载镜像 制作启动U盘

Arch Linux 官方网站 https://www.archlinux.org/

制作启动盘工具 Rufus - 轻松创建 USB 启动盘

Linux下

1
dd if=*iso of /dev/sdb bs=41M

2. 网络连接

参考 Linux配置网络及SSH配置

3. 选择软件源

推荐国内的用户选择http://mirrors.ustc.edu.cn 默认的mirrorlist是开启所有源的,因此我们使用sed先在所有源的前面加上#

1
2
#sed -i "s/^\b/#/g" /etc/pacman.d/mirrorlist
#nano /etc/pacman.d /mirrorlist

将mirrors.ustc.edu.cn前面的#去掉

4. 分区/格式化/挂载

参考 Linux分区

5. 安装基本系统

1. 将基本系统安装到根目录上去

1
#pacstrap /mnt base base-devel
其实,这里安装的基本系统也肯定有自己用不到的冗余功能,例如我就用不到nano文本编辑器,但系统会默认给安上。如果知道基本系统每个文件的作用,其实也完全可以自定义安装。比如:
1
2
#pacstrap /mnt bash coreutils file filesystem grub2 linux pacman \
procps-ng syslog-ng glibc systemd-sysvcompat shawd dhcpcd vi
> 如果你想使用ifconfig之类的工具,请在上面加上net-tools

2. 生成fstab

用下面命令生成 fstab。如果想使用 UUIDs,使用 -U 选项;如果想使用标签,用 -L 选项.

1
#genfstab -U -p /mnt >>/mnt/etc/fstab
> [red]**后面如果出现问题,请不要再次运行genfstab**[red],如果需要,手动编辑/etc/fstab /etc/fstab文件在运行genfstab后应该被检查一下。如果之前你生成了一个EFI系统分区,那么 genfstab给EFI分区添加了错误的选项,会导致无法启动。因此你需要移除EFI分区的所有选项,除了noatime. 对其他分区, 替换"codepage=cp437" 为 "codepage=437" , 会挂载失败导致systemd进入恢复模式。

3. 切换到新系统中

1
2
#arch-chroot /mnt
#sh-4.2#bash
> 到这一步之后,开始系统的主要配置,如果下面文件不存在,需要手动创建。 > 理解并完全安装步骤设置是保证系统配置成功的关键。

4. 对新的基本系统进行设置

写入本机的字符编码方式

1
2
3
#nano /etc/locale.conf #

LANG=en_US.UTF-8 #简略写法 echo LANG= en_US.UTF-8 >> locale.conf

locale.conf 文件默认不存在,一般设置LANG就行了,它是其它设置的默认值。

1
2
3
4
/etc/locale.conf

LANG=zh_CN.UTF-8
LC_TIME=en_GB.UTF-8

修改本机编码

1
# nano /etc/locale.gen  将用不到的编码全删掉,只保留en_US与zh_CN的几行。 

默认情况下 /etc/locale.gen 是一个仅包含注释文档的空文件。选定你需要的本地化类型(移除前面的#即可), 比如中文系统可以使用:

1
2
3
4
5
en_US.UTF-8 UTF-8
zh_CN.GB18030 GB18030
zh_CN.GBK GBK
zh_CN.UTF-8 UTF-8
zh_CN GB2312

对系统的编码进行更新

1
#locale-gen 

写入本机的名称

1
# nano /etc/hostname #简略写法:echo {name} >/etc/hostname,也是一样的。

写入键盘布局方案

1
#nano /etc/vconsole.conf

美式键盘,如下:

1
2
3
KEYMAP=us
FONT=
FONT_MAP=

写入时区

1
2
3
# nano /etc/timezone

Asia/Shanghai

建立时区的软链接

1
#ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

设定系统将用的时间方案

1
#hwclock --systohc --utc

这个时间方案我是试过很多次,如果是双系统,电脑里还有win系统的话,建议设为:–localtime,否则可设为—utc。不过,我现在虽然也用双系统,但还是设的utc,因为设为–localtime虽然在win下时间不会出错,但回到linux下,经常系统会有些古怪的问题,比如,升级系统之时,报密钥错误。使用–utc,虽然在linux下时间会慢8个多小时,但毕竟对整个系统没有影响。

生成内核的启动镜象。

1
#mkinitcpio -p linux

安装必要工具

安装必要的网络工具以便于开机后可以配置网络连接(包括无线)

1
2
3
4
#pacman –S wpa_supplicant net-tools
#pacman -S dialog
#pacman -S netctl
#pacman -S wireless_tools

6. 安装引导

安装grub

1
2
#pacman -S grub-bios os-prober
#grub-install /dev/sda

UEFI 注意分区,参考: Linux分区.格式化.挂载

1
2
#pacman -S grub-bios efibootmgr os-prober
#grub-install --efi-directory=/boot/efi --bootloader-id=arch-grub --target=x86_64-efi

生成启动菜单

1
2
3

#grub-mkconfig -o /boot/grub/grub.cfg
#nano /boot/grub/grub.cfg

生成grub引导windows

如何生成grub引导文件grub.cfg 这里我们需要充分参考点击打开链接grub的说明。首先,需要额外安装一个 os-prober的软件包,直接pacman就行;然后grub-makeconfig 到/boot/grub/grub.cfg 。此时才能生成可以引导多系统的引导文件.如下图。
Window引导

开机自启网络

1
2
#systemctl enable dhcpcd@.service
#dhcpcd

卸载挂载的分区并重启

1
2
#umount /mnt/{boot,home,mnt}
# reboot

基本系统已安装完成

7. 系统配置

忘记安装net-tools补救

1
2
ip link show #查看网卡
ip link set eth0 up # 启用网卡

如果是DHCP的当然简单,直接dhcpcd即可,如果是固定IP的,则要如下操作:

1
2
3
#ip addr add 固定IP/24 dev eth0
#ip link set dev eth0 up
#ip route add default via 网关

系统更新

1
#pacman –Syu

添加用户

1
2
3
#useradd -m 新用户 #新建用户
#passwd 新用户 #指定密码:
#usermod -a -G video,audio,lp,log,wheel,optical,scanner,games,users,storage,power 新用户 #指定用户所在的组

sudo权限

1
nano /etc/sudoers (添加sudo权限)

放开%wheel %sudo权限

sudo命令补全

1
2
#sudo pacman -S bash-completion 
#echo "source /usr/share/bash-completion/bash_completion" >>/home/$USER/.bashrc

更新源列表

1
#pacman -S reflector 

reflector是一个可以从arch官方MirrorStatus列表取回最新mirrorlist的脚本,并且可以根据最新同步时间和速度排序。
下面先说如何自动配置源列表。直接终端输入命令5(注意备份原有源列表)

1
#reflector --verbose --country 'China' -l 200 -p http --sort rate --save /etc/pacman.d/mirrorlist

安装yaourt

1
#vim /etc/pacman.conf
1
2
3
4
[archlinuxcn]
#The Chinese Arch Linux communities packages.
SigLevel = Optional TrustAll
Server = http://repo.archlinuxcn.org/$arch
1
# pacman -Syu yaourt

安装powerpill

1
#yaourt -S powerpill

powerpill是一个可以从多个源多线程下载软件包的程序,类似于迅雷一样,可以明显提升更新速度,相当于pacman的外壳程序,使用方法完全和pacman相同。下面说说powerpill,玩arch的人不知道powerpill是不行的,需要注意的是它也是要调用reflector的,但并不是作为依赖。如果安装reflector后powerpill更新前会默认从mirrorstatus取回45个最新更新的源地址,然后并行下载,否则就是读取/etc/pacman.d/mirrorlist然后配置下载。当然我们推荐第一种,总不能每次都手动执行

7. 驱动显卡

安装显卡驱动

1
2
3
4
5
6
7
# pacman -S mesa
# lspci | grep VGA(查看本机的显卡类型)
# pacman -Ss xf86-video | less(查看能够安装的显卡类型)
# pacman -S …… 安装显卡驱动(或者可以直接所有驱动都自动安装)
# pacman –S xf86-video-vesa
# pacman –S xf86-video-nouveau #如果是ATI显卡的话,要安xf86-video-ati;
# pacman –S virtualbox-guest-utils #虚拟机

安装系统基础程序:

1
2
# pacman -S xorg-server xorg-xinit xorg-utils xorg-server-utils dbus # 先安装x-window服务
# pacman –S xterm xorg-xclock xorg-twm # 安装测试环境

重设系统的编码方式

编辑.xinitrc,把以下内容添加到文件最开始。内可以使用你所喜欢的编辑器,比如nano

1
2
LANG=zh_CN.UTF-8
LC_ALL="zh_CN.UTF-8"

更新系统的编码:

1
#locale-gen

更新一下系统的时间

1
2
# date -s "2013-01-14 14:40:10"
# hwclock --systohc

音频管理

1
# pacman -S alsa-utils pulseaudio-alsa

安装网络管理工具

1
2
3
# pacman –S networkmanager network-manager-applet wireless_tools
# systemctl enable NetworkManager
# systemctl start NetworkManager

安装桌面

击右键菜单,找到文件管理器,然后进入到目录/usr/share/applications/下,你会看到你已经安装完成的程序,全都可以从这儿启动。此时,你不妨复制几个常用的到你的用户目录:/home/新用户/桌面/下去。复制之后,你会在你的桌面上,看到这些程序的启动器。

安装完ibus之后,在/home/$USER/.xinitrc文件中,写入:

1
2
3
4
export GTK_IM_MODULE=ibus
export QT_IM_MODULE=ibus
export XMODIFIERS=@im=ibus
ibus-daemon -d -x

Windows下的磁盘挂载
参考Windows下的磁盘挂载

Xfce主题

字体及补丁

1
2
3
# pacman -S ttf-dejavu ttf-ubuntu-font-family ttf-arphic-ukai ttf-arphic-uming
# pacman -S wqy-microhei wqy-bitmapfont wqy-zenhei ttf-fireflysung
$ yaourt -S cairo-ubuntu libxft-ubuntu freetype2-ubuntu fontconfig-ubuntu #以普通用户身份执行

安装系统主题:

1
sudo pacman -S gtk-aurora-engine gtk-engine-murrine 

鼠标主题:

1
sudo pacman -S xcursor-vanilla-dmz xcursor-vanilla-dmz-aa

图标主题:

1
# pacman -S gnome-icon-theme-extras oxygen-icons human-icon-theme lxde-icon-theme tangerine-icon-theme

针对笔记本电脑的配置:(Speed-step 、 Suspend 等功能)

1
2
# pacman -S  gnome-power-manager  volumeicon
$ yaourt -S laptop-mode-tools pmount

Grub主题
在启动过程中发现Xfce桌面启动载入真心简陋,没有关系,我们在AUR里下载一个balou并设置就好了。

1
$ yaourt -S archlinux-themes-balou

下面来配置grub的启动界面。AUR里有一个非常棒的包grub2-theme-archlinux

1
$ yaourt -S grub2-theme-archlinux

安装后编辑/etc/default/grub,
#GRUB_THEME="/path/to/gfxtheme"改为GRUB_THEME="/boot/grub/themes/Archlinux/theme.txt"
GRUB_GFXMODE=auto改为GRUB_GFXMODE=1024x768修改完成后重新生成一下启动文件

1
# grub-mkconfig -o /boot/grub/grub.cfg

安装 i3 窗口管理器

1
# pacman -S i3

安装 lightdm 显示管理器,

1
# pacman -S lightdm-gtk3-greeter

然后

1
2
# systemctl enable lightdm
# systemctl start lightdm

8. 桌面及美化

本文方案适用于Microsoft Sql Server 2008/2012/2012 r2/2014版本,以下简称MSSQLSERVER。

MSSQL默认是不允许远程连接,并且禁用sa账户的。如果想要在本地用SSMS连接远程服务器上的MSSQLSERVER,需要做两个部分的配置:

1. SQL SERVER MANAGEMENT STUDIO(SSMS)

2. SQL SERVER配置管理器(SQL SERVER CONFIGURATION MANAGER - SSCM)

并且需要注意的是,有些地方如果没有生效,请重启一下sql server(可以从SSCM里,也可以从系统服务中找),下面是详细的步骤:

STEP1. 打开SSMS,使用Windows身份连接数据库,登录后,右键选择“属性”

STEP 2. 选择“安全性”,选中SQL SERVER和Windows身份验证模式

STEP 3. 再选择“连接”,勾选“允许远程连接此服务器”,然后点击“确定”按钮。

STEP 4. 展开“安全性” -》登录名 -》sa,右键选择“属性”

STEP 5. 在“常规”中,改好你自己的密码,这是你sa登录的密码。

STEP 6. 在“状态”中,启用sa登录,点击“确定”按钮

STEP 7. 右键数据库server,选择“方面”

STEP 8. 选择“服务器配置”,找到RemoteAccessEnabled,设置为“True”

STEP 9. 重新启动SQL SERVER服务,退出当前的连接,这时候应该可以用sa进行登录了。

STEP 10. 配置SSCM,选中左侧的“SQL SERVER服务”,确保右侧的“SQL SERVER”以及“SQL SERVER BROWER”正在运行,选择“网络配置”,双击TCP/IP,确保状态为“启用”

STEP 11. 在Client里也确保TCP/IP是启用的,默认的端口都是1433,可以自己修改,非默认端口需要在连接字符串里显式指明。

STEP 12. 到这里再次重启SQL SERVER服务,应该就可以用了。不过还不能用,确认防火墙端口设置,并把SQL SERVER安装目录下,C:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\Binn\sqlservr.exe添加到允许的列表中。

1、安装nuget包MSBuildTasks

2、编辑项目的csproj文件,找到被注释掉的target的beforebuild,去掉注释,添加如下代码。代码如下。

1
2
3
4
5
6
7
8
9
<Target Name="BeforeBuild">
<Version VersionFile="Propertiesversion.txt" Major="1" Minor="0" BuildType="Automatic" StartDate="09/01/2017" RevisionType="BuildIncrement">
<Output TaskParameter="Major" PropertyName="Major" />
<Output TaskParameter="Minor" PropertyName="Minor" />
<Output TaskParameter="Build" PropertyName="Build" />
<Output TaskParameter="Revision" PropertyName="Revision" />
</Version>
<AssemblyInfo CodeLanguage="CS" OutputFile="Properties\FileVersionInfo.cs" AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" />
</Target>     

3、编译项目一次就会在项目文件夹下生成 Propertiesversion.txt

4、在AssemblyInfo.cs文件中包含了AssemblyVersion和AssemblyFileVersion,这里把AssemblyFileVersion单独放到了FileVersionInfo.cs中,编译时会自动生成FileVersionInfo.cs文件,其内容为AssemblyFileVersion,把该文件包含到项目中即可。这里并没有让程序自动生成AssemblyVersion。

这种方式生成版本号会在vs编译时更新版本号。

一、先来看一篇转载文章《在 VS2015 中使用 Qt4

http://tangzx.qiniudn.com/post-0111-qt4-vs2015.html 最早的原文,看不到了

https://github.com/district10/qt4-vs2015x64 原作者的github,里面的东东都下载不了了

二、firecat本人的教程

0、Qt官方

Qt4.8.7官方源码下载

https://download.qt.io/new_archive/qt/4.8/4.8.7/

官网的exe只提供了MSVC2010,没有更高版本的。高版本需要自己下载源码编译。

源码里面的配置文件已经提供了MSVC 2015的编译选项,\qt-everywhere-opensource-src-4.8.7\mkspecs\win32-msvc2015

参照官方提供的编译文档一步一步执行即可;但是配置文件里没有提供MSVC 2017的编译选项。

官方编译的文档

https://doc.qt.io/archives/qt-4.8/installation.html

https://doc.qt.io/archives/qt-4.8/configure-options.html

https://doc.qt.io/archives/qt-4.8/install-win.html

https://doc.qt.io/archives/qt-4.8/install-mac.html

https://doc.qt.io/qt-5/build-sources.html

1、Qt 4.8.7+MSVC 2017

推荐使用第三方提供的源码,它已经是修改好的,里面含有MSVC 2017编译选项,可以编译。

https://github.com/scharsig/Qt Qt4.8.7+MSVC2017源码

https://forum.qt.io/topic/91623/building-qt-4-8-7-with-visual-studio-2017 Qt4.8.7+MSVC2017论坛

https://github.com/sandym/qt-patches 仅供参考,编译补丁

https://github.com/Homebrew/formula-patches/tree/master/qt 仅供参考,编译补丁

https://github.com/BartVandewoestyne/qt_4_8_7_with_vs2017_patch 仅供参考,编译补丁

完整的编译过程:

下载第三方源码https://github.com/scharsig/Qt/tree/master/qt-4.8.7-vs2017 然后解压

-–step1—

Windows桌面-开始-程序-Visual Studio 2017-Visual Studio Tools-VC-x86 Native Tools Command Prompt for VS 2017

-–step2—

C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise>cd F:\Qt\setup-exe\4.8.7\Qt-master\qt-4.8.7-vs2017

-–step3—

F:\Qt\setup-exe\4.8.7\Qt-master\qt-4.8.7-vs2017>configure -help

-–step4—

F:\Qt\setup-exe\4.8.7\Qt-master\qt-4.8.7-vs2017>
configure -make nmake -debug-and-release -opensource -confirm-license -platform win32-msvc2017 -prefix F:\Qt\Qt4.8.7-msvc2017 -nomake examples -nomake tests

如果不想编译这么多功能模块,可以精简为:

configure -make nmake -debug-and-release -opensource -confirm-license -platform win32-msvc2017 -prefix F:\Qt\Qt4.8.7-msvc2017 \
  -no-qt3support -no-multimedia \
  -no-audio-backend -no-phonon -no-phonon-backend -no-libtiff \
  -no-libmng -no-dbus -no-nis -nomake examples -nomake tests

 -release              Compile and link Qt with debugging turned off. -debug                Compile and link Qt with debugging turned on. -nomake tests         Disable building of tests to speed up compilation -nomake examples      Disable building of examples to speed up compilation -confirm-license      Automatically acknowledge the LGPL 2.1 license.

-–step5—

F:\Qt\setup-exe\4.8.7\Qt-master\qt-4.8.7-vs2017>nmake

-–step6—

F:\Qt\setup-exe\4.8.7\Qt-master\qt-4.8.7-vs2017>nmake install

-–step7—

添加到Qt Creator

-–step8—

新建项目测试,Qt Creator+Qt4.8.7+MSVC2017编译项目时,如果报错:

intermediate\moc\moc_rs_actionzoompan.cpp:-1: error: C1041: 无法打开程序数据库“F:\CADCAM\QCAD\src\build-LibreCAD-v1.0.4-qt4-Desktop_Qt_4_8_7_MSVC2017_32bit-Debug\librecad\vc140.pdb”;如果要将多个 CL.EXE 写入同一个 .PDB 文件,请使用 /FS

解决办法:

在Qt Creator的项目文件,即.pro文件中,可以通过QMAKE_CXXFLAGS来给MSVC编译器传递编译开关。

QMAKE_CXXFLAGS += /FS

win32-msvc*:QMAKE_CXXFLAGS += /wd"4819" QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings 

MSVC 2017编译器常见错误的解决:

https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/c-cpp-build-errors?view=vs-2017

2、Mac OS+Qt 4.8.7

笔者的Mac OS版本是MacOS-10.15-Catalina,高版本的OS和Clang已经不再支持Qt官方发布的Qt4了。

解决办法可以参见我的另一篇博文:https://blog.csdn.net/libaineu2004/article/details/104740623

https://trac.macports.org/ticket/58651 mac下编译qt4遇到问题

https://github.com/macports/macports-ports/tree/master/aqua/qt4-mac mac编译补丁

解决路径显示数字问题

  • 命令及现象
1
git status # 当提交文件中有中文目录时,目录会显示为数字
  • 原因

    在默认设置下中文名不能正常显示,而是显示为八进制的字符编码。

  • 解决办法

    修改git配置文件core.quotepath 为false

    1
    2
    # --global 表示全局配置
    git config --global core.quotepath false

    终端输出中文为乱码

    • 命令及现象

      1
      git log # 当log中有中文日志时,日志显示为乱码
    • 原因

      git commit 默认的编码是UTF-8 ,cmd默认的编码是GB-2312,字符集不同显示为乱码。

    • 解决办法

      1
      2
      3
      4
      5
      # 设置GUI编码为UTF-8
      git config --global gui.encoding utf-8
      # 设置提交日志编码为UTF-8
      git config --global i18n.commitencoding utf-8

配置文件

配置文件默认存放路径

1
2
C:\Users\[用户名]\.gitconfig # 全局配置文件存放路径,修改此文件效果等价git config --global
[存储库根目录]\.git\config # 当前存储库配置文件,只影响当前存储库,效果等价于在此存储库执行 git config
1
2
3
4
5
6
7
8
# 全局设置
[gui]
encoding = utf-8
[core]
quotePath = false
[i18n]
commitencoding = utf-8

非常详细的 Docker 学习笔记 - Docker - 服务器软件 - 深度开源 (open-open.com)

Docker-创建和分享应用(3) - 头痛不头痛 - 博客园 (cnblogs.com)

常用docker命令,及一些坑_dockerfile 2>&1-CSDN博客

Docker常用命令_docker -i -t -v-CSDN博客

Docker 常用命令 - Phuker’s Blog

详解Docker 容器基础系统镜像打包_docker_脚本之家 (jb51.net)

一种docker基础镜像制作方法_docker 从iso制作镜像-CSDN博客

系统运维|两种方式创建你自己的 Docker 基本映像 (linux.cn)

Windows Azure 是微软基于云计算的操作系统,能够为开发者提供一个平台,帮助开发可运行在云服务器、数据中心、Web 和 PC 上的应用程序。

Azure 是一种灵活和支持互操作的平台,能够将处于云端的开发者个人能力,同微软全球数据中心网络托管的服务,比如存储、计算和网络基础设施服务,紧密结合起来。帮助开发者在“云端”和“客户端”同时部署应用,使得企业与用户都能共享资源。

本文整理了丰富的 Windows Azure 学习资源,帮助开发者能全面地学习 Windows Azure 知识,并将 Windows Azure 运用在项目和实际工作中。

通过本系列博客,先来了解一下 Windows Azure 平台的基本知识。Windows Azure,正如同桌面操作系统 Windows 和服务器操作系统 Windows Server 一样,是一个云端的操作系统。开发人员可以使用同一套技术:.NET(包括 Silverlight),或者 Win32,同时针对桌面,服务器,以及云,开发程序,而不需要针对某个平台学习专门的技术。Visual Studio 和 Expression Studio 为开发人员提供了强大的工具支持。

Windows Azure平台简介(一):定位与产品结构

Windows Azure平台简介(二):Windows Azure

Windows Azure平台简介(三):AppFabric

Windows Azure平台简介(四):SQL Azure以及其他服务

在开始本教学之前,请确保你从 Windows Azure 平台下载下载并安装了最新的 Windows Azure 开发工具。本教学使用 Visual Studio 2010 作为开发工具。

Windows Azure入门教学系列 (一):创建第一个WebRole程序

Windows Azure入门教学系列 (二):部署第一个Web Role程序

Windows Azure入门教学系列 (三):创建第一个Worker Role程序

Windows Azure入门教学系列 (四):使用Blob Storage

Windows Azure入门教学系列 (五):使用Queue Storage

Windows Azure入门教学系列 (六):使用Table Storage

Windows Azure入门教学系列 (七):使用REST API访问Storage Service

Windows Azure入门教学系列 (八):使用Windows Azure Drive

Azure学习笔记:Web Site(1)

Azure学习笔记:Service Bus(2)

Azure学习笔记:Storage(3)

Azure学习笔记:Cloud Service(4)

Azure Storage 是微软 Azure 云提供的云端存储解决方案,当前支持的存储类型有 Blob、Queue、File 和 Table。

Azure Blob Storage 基本用法 – Azure Storage 之 Blob

Azure Queue Storage 基本用法 – Azure Storage 之 Queue

Azure File Storage 基本用法 – Azure Storage 之 File

Azure Table storage 基本用法 – Azure Storage 之 Table

Windows Azure Storage 支持三重冗余的。保存在 Azure Storage 的内容,会在同一个数据中心保留有3个副本。这样的好处显而易见:当数据中心发生一般性故障的时候,比如磁盘损坏,机架服务器损坏等,用户保存在 Azure Storage 的数据不会丢失。每次对于 Storage 的写操作,都会对三个副本进行同步写操作,等到在副本操作完毕之后,才会返回执行成功给客户端。

Windows Azure 提供了三种不同类型的存储服务(这里的存储是非关系型数据,比如图片、文档等文件),用来提供给 Windows Azure 上运行的应用程序存储数据使用。依据不同的存储格式会有不同的限制,因为这些存储服务都是以分散式巨量存储(Distributed Mass Storage)为核心概念所设计出来的,为了要达成快速在分散式存储空间中存储与管理数据(还包含高可用度的赘余存储管理),微软有在数据的存储上做一些限制。

微软还提供了 REST API 来方便用户操作 Storage Service。

(1)Windows Azure Storage Service存储服务

(2)Windows Azure Storage Service存储服务之Blob详解(上)

(3)Windows Azure Storage Service存储服务之Blob详解(中)

(4)Windows Azure Storage Service存储服务之Blob Share Access Signature

(5)Windows Azure Drive

(6)Windows Azure Storage之Table

(7)使用工具管理Windows Azure Storage

(8)Windows Azure 上的托管服务CDN (上)

(9)Windows Azure 上的托管服务CDN (中) Blob Service

(10)Windows Azure 上的托管服务CDN (下) Hosted Service

(11)计算你存储的Blob的大小

(12)本地冗余存储 vs 地理冗余存储 (上)

(13)本地冗余存储 vs 地理冗余存储 (下)

(14)使用Azure Blob的PutBlock方法,实现文件的分块、离线上传

(15)使用WCF服务,将本地图片上传至Azure Storage (上) 服务器端代码

(16)使用WCF服务,将本地图片上传至Azure Storage (上) 客户端代码

(17)Azure Storage读取访问地域冗余(Read Access – Geo Redundant Storage, RA-GRS)

(18)使用HTML5 Portal的Azure CDN服务

(19)再谈Azure Block Blob和Page Blob

(20)使用Azure File实现共享文件夹

(21)使用AzCopy工具,加快Azure Storage传输速度

(22)Azure Storage如何支持多级目录

(23)计算Azure VHD实际使用容量

PowerShell 是管理 Azure 的最好方式之一,通过使用 PowerShell 脚本可以把很多的工作自动化。比如对于 Azure 上的虚拟机,可以设置定时关机操作,并在适当的时间把它开机,这样就能减少虚拟机的运行时间,同时也能为节能减排做出贡献。

(1)PowerShell入门

(2)修改Azure订阅名称

(3)上传证书

(4)使用PowerShell管理多个订阅

(5)使用Azure PowerShell创建简单的Azure虚拟机和Linux虚拟机

(6)设置单个Virtual Machine Endpoint

(7)使用CSV文件批量设置Virtual Machine Endpoint

(8)使用PowerShell设置Azure负载均衡器规则

(9)使用PowerShell导出订阅下所有的Azure VM的Public IP和Private IP

(10)使用PowerShell导出订阅下所有的Azure VM和Cloud Service的高可用情况

(11)使用自定义虚拟机镜像模板,创建Azure虚拟机并绑定公网IP(VIP)和内网IP(DIP)

(12)通过Azure PowerShell创建SSH登录的Linux VM

SQL Azure 是微软基于 Microsoft SQL Server Denali,也就是 SQL Server 2012 构建的云端关系型数据库服务。SQL Azure 是 SQL Server 的一个大子集,能够实现 SQL Server 的绝大部分功能,并且将它们作为云端的服务来扩展。SQL Azure Database 提供内置的高精准、可用性、功效与其他功能。

(1)入门

(2)SQL Azure vs SQL Server

(3)创建一个SQL Azure 服务器

(4)创建一个SQL Azure数据库

(5)使用SQL Server Management Studio连接SQL Azure

(6)使用Project Houston管理SQL Azure

(7)在SQL Azure Database中执行的T-SQL

(8)使用Visual Studio 2010开发应用连接SQL Azure云端数据库

(9)把本地的SQL Server数据库迁移到SQL Azure云数据库上

(10)SQL Azure Data Sync数据同步功能(上)

(11)SQL Azure Data Sync数据同步功能(下)

(12)使用新Portal 创建 SQL Azure Database

(13)Azure的两种关系型数据库服务:SQL Azure与SQL Server VM的不同

(14)将云端SQL Azure中的数据库备份到本地SQL Server

(15)SQL Azure 新的规格

(16)创建PaaS SQL Azure V12数据库

(17)SQL Azure V12 - 跨数据中心标准地域复制(Standard Geo-Replication)

(18)使用External Table实现垮库查询

(19)Stretch Database 概览

(20)使用SQL Server 2016 Upgrade Advisor

(21)将整张表都迁移到Azure Stretch Database里

(22)迁移部分数据到Azure Stretch Database

1. 《Windows Azure 实战》全面深入,完整覆盖 Windows Azure 所有关键技术和理论,详细讲解云计算开发流程、云服务架构(可用性、可靠性和高性能)、云设备整合、系统整合,以及云计算项目的管理。
注重实战,68个精心策划的针对特定实际应用场景的真实案例,详细呈现案例的设计思路和完整实现步骤。

2. 《Windows Azure 从入门到精通》介绍了如何构建和管理云端的可扩展应用,一次一个知识点,同时辅之以适当的练习,可帮助读者轻松掌握基本的编程技能,掌握 Windows Azure 云计算平台的核心服务和特性,是一本理想的入门教程。

3. 《云计算与Azure平台实战》解决了从本地转移到基于云的应用程序时,可能面临的各种问题;展示了如何将 ASP.NET 身份验证和角色管理用应用于 Azure Web 角色;揭示了迁移到 Windows Azure 时把计算服务卸载到一个或多个 WorkerWeb 角色的益处;讲解如何为共享 Azure 表选择最合适的 PartionKey 和 RowKey 值的组合;探讨了改善 Azure 表的可扩展性和性能的方法。

4. 《走进云计算:Windows Azure实战手记》介绍了你必须学会的微软云开发技术,介绍目前最火爆的云计算,深入剖析微软最新的云开发平台,涵盖 Windows Azure 环境、存储服务、SQL Azure 数据库与 App Fabric 服务平台 Step by Step 递进教学,初学者可按部就班地学习云应用的开发技术。

相关阅读:

Azure Blob Storage 基本用法 – Azure Storage 之 Blob

Azure Queue Storage 基本用法 – Azure Storage 之 Queue

Azure File Storage 基本用法 – Azure Storage 之 File

Azure Table storage 基本用法 – Azure Storage 之 Table

一、需求缘起

几乎所有的业务系统,都有生成一个记录标识的需求,例如:

(1)消息标识:message-id

(2)订单标识:order-id

(3)帖子标识:tiezi-id

这个记录标识往往就是数据库中的唯一主键,数据库上会建立聚集索引(cluster index),即在物理存储上以这个字段排序。

这个记录标识上的查询,往往又有分页或者排序的业务需求,例如:

(1)拉取最新的一页消息:selectmessage-id/ order by time/ limit 100

(2)拉取最新的一页订单:selectorder-id/ order by time/ limit 100

(3)拉取最新的一页帖子:selecttiezi-id/ order by time/ limit 100

所以往往要有一个time字段,并且在time字段上建立普通索引(non-cluster index)。

我们都知道普通索引存储的是实际记录的指针,其访问效率会比聚集索引慢,如果记录标识在生成时能够基本按照时间有序,则可以省去这个time字段的索引查询:

select message-id/ (order by message-id)/limit 100

再次强调,能这么做的前提是,message-id的生成基本是趋势时间递增的

这就引出了记录标识生成(也就是上文提到的三个XXX-id)的两大核心需求:

(1)全局唯一

(2)趋势有序

这也是本文要讨论的核心问题:如何高效生成趋势有序的全局唯一ID。

二、常见方法、不足与优化

【常见方法一:使用数据库的 auto_increment 来生成全局唯一递增ID】

优点:

(1)简单,使用数据库已有的功能

(2)能够保证唯一性

(3)能够保证递增性

(4)步长固定

缺点:

(1)可用性难以保证:数据库常见架构是一主多从+读写分离,生成自增ID是写请求,主库挂了就玩不转了

(2)扩展性差,性能有上限:因为写入是单点,数据库主库的写性能决定ID的生成性能上限,并且难以扩展

改进方法:

(1)增加主库,避免写入单点

(2)数据水平切分,保证各主库生成的ID不重复

如上图所述,由1个写库变成3个写库,每个写库设置不同的auto_increment初始值,以及相同的增长步长,以保证每个数据库生成的ID是不同的(上图中库0生成0,3,6,9…,库1生成1,4,7,10,库2生成2,5,8,11…)

改进后的架构保证了可用性,但缺点是:

(1)丧失了ID生成的“绝对递增性”:先访问库0生成0,3,再访问库1生成1,可能导致在非常短的时间内,ID生成不是绝对递增的(这个问题不大,我们的目标是趋势递增,不是绝对递增)

(2)数据库的写压力依然很大,每次生成ID都要访问数据库

为了解决上述两个问题,引出了第二个常见的方案

【常见方法二:单点批量ID生成服务】

分布式系统之所以难,很重要的原因之一是“没有一个全局时钟,难以保证绝对的时序”,要想保证绝对的时序,还是只能使用单点服务,用本地时钟保证“绝对时序”。数据库写压力大,是因为每次生成ID都访问了数据库,可以使用批量的方式降低数据库写压力。

如上图所述,数据库使用双master保证可用性,数据库中只存储当前ID的最大值,例如0。ID生成服务假设每次批量拉取6个ID,服务访问数据库,将当前ID的最大值修改为5,这样应用访问ID生成服务索要ID,ID生成服务不需要每次访问数据库,就能依次派发0,1,2,3,4,5这些ID了,当ID发完后,再将ID的最大值修改为11,就能再次派发6,7,8,9,10,11这些ID了,于是数据库的压力就降低到原来的1/6了。

优点

(1)保证了ID生成的绝对递增有序

(2)大大的降低了数据库的压力,ID生成可以做到每秒生成几万几十万个

缺点

(1)服务仍然是单点

(2)如果服务挂了,服务重启起来之后,继续生成ID可能会不连续,中间出现空洞(服务内存是保存着0,1,2,3,4,5,数据库中max-id是5,分配到3时,服务重启了,下次会从6开始分配,4和5就成了空洞,不过这个问题也不大)

(3)虽然每秒可以生成几万几十万个ID,但毕竟还是有性能上限,无法进行水平扩展

改进方法

单点服务的常用高可用优化方案是“备用服务”,也叫“影子服务”,所以我们能用以下方法优化上述缺点(1):

如上图,对外提供的服务是主服务,有一个影子服务时刻处于备用状态,当主服务挂了的时候影子服务顶上。这个切换的过程对调用方是透明的,可以自动完成,常用的技术是vip+keepalived,具体就不在这里展开。

 

【常见方法三:uuid】

上述方案来生成ID,虽然性能大增,但由于是单点系统,总还是存在性能上限的。同时,上述两种方案,不管是数据库还是服务来生成ID,业务方Application都需要进行一次远程调用,比较耗时。有没有一种本地生成ID的方法,即高性能,又时延低呢?

uuid是一种常见的方案:string ID =GenUUID();

优点

(1)本地生成ID,不需要进行远程调用,时延低

(2)扩展性好,基本可以认为没有性能上限

缺点

(1)无法保证趋势递增

(2)uuid过长,往往用字符串表示,作为主键建立索引查询效率低,常见优化方案为“转化为两个uint64整数存储”或者“折半存储”(折半后不能保证唯一性)

【常见方法四:取当前毫秒数】

uuid是一个本地算法,生成性能高,但无法保证趋势递增,且作为字符串ID检索效率低,有没有一种能保证递增的本地算法呢?

取当前毫秒数是一种常见方案:uint64 ID = GenTimeMS();

优点

(1)本地生成ID,不需要进行远程调用,时延低

(2)生成的ID趋势递增

(3)生成的ID是整数,建立索引后查询效率高

缺点

(1)如果并发量超过1000,会生成重复的ID

我去,这个缺点要了命了,不能保证ID的唯一性。当然,使用微秒可以降低冲突概率,但每秒最多只能生成1000000个ID,再多的话就一定会冲突了,所以使用微秒并不从根本上解决问题。

【常见方法五:类snowflake算法】

snowflake是twitter开源的分布式ID生成算法,其核心思想是:一个long型的ID,使用其中41bit作为毫秒数,10bit作为机器编号,12bit作为毫秒内序列号。这个算法单机每秒内理论上最多可以生成1000*(2^12),也就是400W的ID,完全能满足业务的需求。

借鉴snowflake的思想,结合各公司的业务逻辑和并发量,可以实现自己的分布式ID生成算法。

举例,假设某公司ID生成器服务的需求如下:

(1)单机高峰并发量小于1W,预计未来5年单机高峰并发量小于10W

(2)有2个机房,预计未来5年机房数量小于4个

(3)每个机房机器数小于100台

(4)目前有5个业务线有ID生成需求,预计未来业务线数量小于10个

(5)…

分析过程如下:

(1)高位取从2016年1月1日到现在的毫秒数(假设系统ID生成器服务在这个时间之后上线),假设系统至少运行10年,那至少需要10年*365天*24小时*3600秒*1000毫秒=320*10^9,差不多预留39bit给毫秒数

(2)每秒的单机高峰并发量小于10W,即平均每毫秒的单机高峰并发量小于100,差不多预留7bit给每毫秒内序列号

(3)5年内机房数小于4个,预留2bit给机房标识

(4)每个机房小于100台机器,预留7bit给每个机房内的服务器标识

(5)业务线小于10个,预留4bit给业务线标识

这样设计的64bit标识,可以保证:

(1)每个业务线、每个机房、每个机器生成的ID都是不同的

(2)同一个机器,每个毫秒内生成的ID都是不同的

(3)同一个机器,同一个毫秒内,以序列号区区分保证生成的ID是不同的

(4)将毫秒数放在最高位,保证生成的ID是趋势递增的

缺点

(1)由于“没有一个全局时钟”,每台服务器分配的ID是绝对递增的,但从全局看,生成的ID只是趋势递增的(有些服务器的时间早,有些服务器的时间晚)

最后一个容易忽略的问题

生成的ID,例如message-id/ order-id/ tiezi-id,在数据量大时往往需要分库分表,这些ID经常作为取模分库分表的依据,为了分库分表后数据均匀,ID生成往往有“取模随机性”的需求,所以我们通常把每秒内的序列号放在ID的最末位,保证生成的ID是随机的。

又如果,我们在跨毫秒时,序列号总是归0,会使得序列号为0的ID比较多,导致生成的ID取模后不均匀。解决方法是,序列号不是每次都归0,而是归一个0到9的随机数,这个地方。

下面附上C#.Net 实现snowflake算法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CommonTools
{
public class SnowFlake
{
//机器ID
private static long workerId;
private static long twepoch = 687888001020L; //唯一时间,这是一个避免重复的随机量,自行设定不要大于当前时间戳
private static long sequence = 0L;
private static int workerIdBits = 4; //机器码字节数。4个字节用来保存机器码
public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大机器ID
private static int sequenceBits = 10; //计数器字节数,10个字节用来保存计数码
private static int workerIdShift = sequenceBits; //机器码数据左移位数,就是后面计数器占用的位数
private static int timestampLeftShift = sequenceBits + workerIdBits; //时间戳左移动位数就是机器码和计数器总字节数
public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成
private long lastTimestamp = -1L;

private static SnowFlake sigle = null;

private SnowFlake(long workerId)
{
if (workerId > maxWorkerId || workerId < 0)
throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0 ", workerId));
SnowFlake.workerId = workerId;
}

public static long NewID()
{
if (sigle == null)
{
sigle = new SnowFlake(4L); //此处4L应该从配置文件里读取当前机器配置

}

return sigle.nextId();


}



private long nextId()
{
lock (this)
{
long timestamp = timeGen();
if(this.lastTimestamp == timestamp){ //同一微妙中生成ID
//用&运算计算该微秒内产生的计数是否已经到达上限
SnowFlake.sequence = (SnowFlake.sequence + 1) & SnowFlake.sequenceMask;
if (SnowFlake.sequence == 0)
{
//一微妙内产生的ID计数已达上限,等待下一微妙
timestamp = tillNextMillis(this.lastTimestamp);
}
}
else{ //不同微秒生成ID
SnowFlake.sequence = 0; //计数清0
}
if(timestamp < lastTimestamp)
{ //如果当前时间戳比上一次生成ID时时间戳还小,抛出异常,因为不能保证现在生成的ID之前没有生成过
throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds",
this.lastTimestamp - timestamp));
}
this.lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳
long nextId = (timestamp - twepoch << timestampLeftShift)
| SnowFlake.workerId << SnowFlake.workerIdShift | SnowFlake.sequence;
return nextId;
}
}

/// <summary>
/// 获取下一微秒时间戳
/// </summary>
/// <param name="lastTimestamp"></param>
/// <returns></returns>
private long tillNextMillis(long lastTimestamp)
{
long timestamp = timeGen();
while(timestamp <= lastTimestamp)
{
timestamp = timeGen();
}
return timestamp;
}

/// <summary>
/// 生成当前时间戳
/// </summary>
/// <returns></returns>
private long timeGen()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
}

}
}

Shiro

  • 登陆、授权、拦截
  • 按钮权限控制

一、目标

  • Maven+Spring+shiro
  • 自定义登陆、授权
  • 自定义拦截器
  • 加载数据库资源构建拦截链

使用总结:

1、需要设计的数据库:用户、角色、权限、资源

2、可以通过,角色,权限,两个拦截器同时确定是否能访问

3、角色与权限的关系,role1=permission1,permission2,多级的权限:sys:permission1,拥有高级权限同时用于低级权限。

4、perms[“permission1”] 为权限

5、拦截器机制介绍了拦截角色还是权限

6、角色与权限 是两个概念

7、权限-资源,一对一。资源分为上下级,因此权限分为父权限,子权限。创建资源的时候,创建权限。权限里资源的别名

8、角色-权限,一对多。角色里权限的别名

9、按钮是通过权限来控制的

10、防止有父级资源可以访问,子级资源不能访问的情况,不适用 sys:add 权限写法

二、代码

1、Pom.xml

复制代码

1 <properties>
2 <spring.version>4.3.4.RELEASE</spring.version>
3 </properties>
4 <dependency>
5 <groupId>junit</groupId>
6 <artifactId>junit</artifactId>
7 <version>4.9</version>
8 </dependency>
9 <dependency>
10 <groupId>commons-logging</groupId>
11 <artifactId>commons-logging</artifactId>
12 <version>1.1.3</version>
13 </dependency>
14 <dependency>
15 <groupId>org.apache.shiro</groupId>
16 <artifactId>shiro-core</artifactId>
17 <version>1.2.2</version>
18 </dependency>
19 <dependency>
20 <groupId>org.apache.shiro</groupId>
21 <artifactId>shiro-spring</artifactId>
22 <version>1.2.2</version>
23 </dependency>
24 <dependency>
25 <groupId>javax.servlet</groupId>
26 <artifactId>javax.servlet-api</artifactId>
27 <version>3.0.1</version>
28 <scope>provided</scope>
29 </dependency>
30 <dependency>
31 <groupId>org.springframework</groupId>
32 <artifactId>spring-web</artifactId>
33 <version>${spring.version}</version>
34 </dependency>
35
36 org.apache.shiro
37 shiro-ehcache
38 1.2.2
39

40
41 org.springframework
42 spring-context
43 ${spring.version}
44

45
46 org.apache.shiro
47 shiro-web
48 1.2.2
49

50
51 net.sf.ehcache
52 ehcache
53 2.10.1
54

复制代码

2、web.xml

  Servlet拦截访问,使用注解更方便,需要删除项目中的servlet使用javax.servlet-api 3.0 包

复制代码

1 package com.cyd.shiro;
2
3 import java.io.IOException;
4
5 import javax.servlet.ServletException;
6 import javax.servlet.annotation.WebServlet;
7 import javax.servlet.http.HttpServlet;
8 import javax.servlet.http.HttpServletRequest;
9 import javax.servlet.http.HttpServletResponse;
10
11 import org.apache.shiro.SecurityUtils;
12 import org.apache.shiro.authc.AuthenticationException;
13 import org.apache.shiro.authc.IncorrectCredentialsException;
14 import org.apache.shiro.authc.UnknownAccountException;
15 import org.apache.shiro.authc.UsernamePasswordToken;
16 import org.apache.shiro.subject.Subject;
17 import org.apache.shiro.web.util.SavedRequest;
18 import org.apache.shiro.web.util.WebUtils;
19 import org.junit.Test;
20
21 @WebServlet(name = “loginServlet”, urlPatterns = “/loginController”)
22 public class LoginServlet extends HttpServlet {
23 @Override
24 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
25 req.getRequestDispatcher(“login.jsp”).forward(req, resp);
26 }
27
28 @Override
29 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
30 System.out.println(LoginServlet.class.toString());
31 String error = null;
32 String username = req.getParameter(“username”);
33 String password = req.getParameter(“password”);
34 Subject subject = SecurityUtils.getSubject();
35 UsernamePasswordToken token = new UsernamePasswordToken(username, password);
36 try {
37 subject.login(token);
38 } catch (UnknownAccountException e) {
39 error = “用户名/密码错误”;
40 } catch (IncorrectCredentialsException e) {
41 error = “用户名/密码错误”;
42 } catch (AuthenticationException e) {
43 // 其他错误,比如锁定,如果想单独处理请单独catch处理
44 error = “其他错误:” + e.getMessage();
45 }
46 if (error != null) {// 出错了,返回登录页面
47 req.setAttribute(“error”, error);
48 req.getRequestDispatcher(“login.jsp”).forward(req, resp);
49 } else {// 登录成功
50 //跳转到拦截登陆前的地址
51 SavedRequest request=WebUtils.getSavedRequest(req);
52 String url =request.getRequestURI();
53 req.getRequestDispatcher(url.substring(url.lastIndexOf(‘/‘))).forward(req, resp);
54 }
55 }
56
57 }

复制代码

3、Spring-shiro.xml

复制代码

<beans xmlns=“http://www.springframework.org/schema/beans“ xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“ xmlns:context=“http://www.springframework.org/schema/context“ xmlns:util=“http://www.springframework.org/schema/util“ xsi:schemaLocation=“http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.2.xsd"\>

<context:component-scan base-package\="com.cyd.shiro.\*"\></context:component-scan\>

<!-- Shiro的Web过滤器 \-->
<bean id\="shiroFilter" class\="com.cyd.shiro.ExtendShiroFilterFactoryBean"\>
    <property name\="securityManager" ref\="securityManager" />
    <property name\="loginUrl" value\="/login.jsp" />
    <!-- <property name="successUrl" value="/index.jsp" /> \-->
    <property name\="unauthorizedUrl" value\="/unauthorized.jsp" />
    <property name\="filters"\>
        <util:map\>
            <!-- <entry key="onperms" value-ref="URLPermissionsFilter" /> \-->
            <entry key\="onrole" value-ref\="ExtendRolesAuthorizationFilter" />
        </util:map\>
    </property\> 
    <property name\="filterChainDefinitions"\>
        <value\> /unauthorized.jsp = anon
            /logoutController=anon
            /login.jsp=authc
        </value\>
    </property\>
</bean\>

<!-- 安全管理器 \-->
<bean id\="securityManager" class\="org.apache.shiro.web.mgt.DefaultWebSecurityManager"\>
    <property name\="realm" ref\="myRealm" />
    <property name\="cacheManager" ref\="cacheManager" />
</bean\>
<!-- 自定义认证,授权 \-->
<bean id\="myRealm" class\="com.cyd.shiro.AdminRealm"\></bean\>

<!-- 注册ehcache,不然每次访问都要登陆 \-->
<bean id\="cacheManager" class\="org.apache.shiro.cache.ehcache.EhCacheManager"\>
    <property name\="cacheManagerConfigFile" value\="classpath:ehcache.xml" />
</bean\>
<!-- 自定义鉴权拦截器 \-->
<bean id\="URLPermissionsFilter" class\="com.cyd.shiro.URLPermissionsFilter" />
<bean id\="ExtendRolesAuthorizationFilter" class\="com.cyd.shiro.ExtendRolesAuthorizationFilter" />

</beans>

复制代码

4、Ehcache.xml 缓存

复制代码

<ehcache xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“ xsi:noNamespaceSchemaLocation=“../config/ehcache.xsd”>
<diskStore path=“java.io.tmpdir”/>
<defaultCache
maxElementsInMemory=“10000” eternal=“false” timeToIdleSeconds=“600” timeToLiveSeconds=“600” overflowToDisk=“true” maxElementsOnDisk=“10000000” diskPersistent=“false” diskExpiryThreadIntervalSeconds=“120” memoryStoreEvictionPolicy=“LRU”
/>

</ehcache>

复制代码

5、登陆Servlet

复制代码

package com.cyd.shiro;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;

@WebServlet(name = “loginServlet”, urlPatterns = “/loginController”)
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher(“login.jsp”).forward(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println(LoginServlet.class.toString());
    String error = null;
    String username = req.getParameter("username");
    String password = req.getParameter("password");
    Subject subject = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    try {
        subject.login(token); 
    } catch (UnknownAccountException e) { 
        error = "用户名/密码错误";
    } catch (IncorrectCredentialsException e) {
        error = "用户名/密码错误";
    } catch (AuthenticationException e) {
        // 其他错误,比如锁定,如果想单独处理请单独catch处理
        error = "其他错误:" + e.getMessage();
    }
    if (error != null) {// 出错了,返回登录页面
        req.setAttribute("error", error);
        req.getRequestDispatcher("login.jsp").forward(req, resp);
    } else {// 登录成功
        //跳转到拦截登陆前的地址
        SavedRequest request=WebUtils.getSavedRequest(req);
        String url =request.getRequestURI();
        req.getRequestDispatcher(url.substring(url.lastIndexOf('/'))).forward(req, resp);
    }
}

}

复制代码

6、自定义登陆、授权。

  根据需求自定义登陆异常。从数据库查询出当前用户拥有的权限并授权

复制代码

1 package com.cyd.shiro;
2
3 import java.util.HashSet;
4 import java.util.LinkedList;
5 import java.util.List;
6 import java.util.Set;
7
8 import org.apache.shiro.authc.AuthenticationException;
9 import org.apache.shiro.authc.AuthenticationInfo;
10 import org.apache.shiro.authc.AuthenticationToken;
11 import org.apache.shiro.authc.SimpleAuthenticationInfo;
12 import org.apache.shiro.authc.UnknownAccountException;
13 import org.apache.shiro.authz.AuthorizationInfo;
14 import org.apache.shiro.authz.SimpleAuthorizationInfo;
15 import org.apache.shiro.realm.AuthorizingRealm;
16 import org.apache.shiro.subject.PrincipalCollection;
17 import org.springframework.beans.factory.annotation.Autowired;
18
19 import com.cyd.helloworld.SysRoles;
20 import com.cyd.helloworld.SysUsers;
21 import com.cyd.shiro.admin.SysUsersService;
22
23 public class AdminRealm extends AuthorizingRealm {
24
25 @Autowired
26 private SysUsersService sysusersservice;
27 // 认证登陆
28 @Override
29 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
30 System.out.println(“do doGetAuthenticationInfo”);
31 String username = (String) token.getPrincipal();
32 SysUsers user = sysusersservice.getSysUsers(username);
33 if (user == null) {
34 throw new UnknownAccountException();// 没找到帐号
35 }
36 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUserName(), // 用户名
37 user.getPassWorld(), // 密码
38 getName() // realm name
39 );
40 return authenticationInfo;
41 }
42
43 // 用户授权
44 @Override
45 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
46 System.out.println(“do doGetAuthorizationInfo”);
47 String username = (String)principals.getPrimaryPrincipal();
48 SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
49 //从数据库加载当前用户的角色,例如:[admin]
50 authorizationInfo.setRoles(new HashSet(sysusersservice.getSysRoles(username)));
51 //从数据库加载当前用户可以访问的资源,例如:[index.jsp, abc.jsp]
52 authorizationInfo.setStringPermissions(new HashSet(sysusersservice.getSysResource(username)));
53
54 return authorizationInfo;
55 }
56 }

复制代码

7、自定义拦截器。

  重写拦截器是因为shiro 验证是否有权限访问是需要当前用户拥有拦截器链的所有权限。一般需求只需要拥有部分权限即可。

       角色验证拦截,hasRole和hasAllRoles 验证是否有权限。

复制代码

1 package com.cyd.shiro;
2
3 import java.io.IOException;
4 import java.util.Set;
5
6 import javax.servlet.ServletRequest;
7 import javax.servlet.ServletResponse;
8
9 import org.apache.shiro.subject.Subject;
10 import org.apache.shiro.util.CollectionUtils;
11 import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
12
13 /**
14 * 通过角色验证权限
15 * @author chenyd
16 * 2017年11月21日
17 */
18 public class ExtendRolesAuthorizationFilter extends RolesAuthorizationFilter{
19
20 @Override
21 public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
22
23 System.out.println(ExtendRolesAuthorizationFilter.class.toString());
24 Subject subject = getSubject(request, response);
25 String[] rolesArray = (String[]) mappedValue;
26
27 if (rolesArray == null || rolesArray.length == 0) {
28 //no roles specified, so nothing to check - allow access.
29 return true;
30 }
31 //AbstractFilter
32 Set roles = CollectionUtils.asSet(rolesArray);
33
34 boolean flag=false;
35 for(String role: roles){
36 if(subject.hasRole(role)){
37 flag=true;
38 break;
39 }
40 }
41 return flag;
42 }
43 }

复制代码

       url拦截校验,isPermitted和isPermittedAll验证是否有权限访问,

复制代码

1 package com.cyd.shiro;
2
3 import java.io.IOException;
4
5 import javax.servlet.ServletRequest;
6 import javax.servlet.ServletResponse;
7 import javax.servlet.http.HttpServletRequest;
8
9 import org.apache.shiro.subject.Subject;
10 import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
11 /**
12 * 通过字符串验证权限
13 * @author chenyd
14 * 2017年11月21日
15 */
16 public class URLPermissionsFilter extends PermissionsAuthorizationFilter {
17
18 /**
19 * mappedValue 访问该url时需要的权限
20 * subject.isPermitted 判断访问的用户是否拥有mappedValue权限
21 * 重写拦截器,只要符合配置的一个权限,即可通过
22 */
23 @Override
24 public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
25 throws IOException {
26 System.out.println(URLPermissionsFilter.class.toString());
27 Subject subject = getSubject(request, response);
28 // DefaultFilterChainManager
29 // PathMatchingFilterChainResolver
30 String[] perms = (String[]) mappedValue;
31 boolean isPermitted = false;
32 if (perms != null && perms.length > 0) {
33 for (String str : perms) {
34 if (subject.isPermitted(str)) {
35 isPermitted = true;
36 }
37 }
38 }
39
40 return isPermitted;
41 }
42 }

复制代码

8、加载数据库资源构建拦截器链

复制代码

1 package com.cyd.shiro;
2
3 import java.util.Map;
4
5 import org.apache.shiro.config.Ini;
6 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
7 import org.apache.shiro.util.CollectionUtils;
8 import org.apache.shiro.web.config.IniFilterChainResolverFactory;
9 import org.springframework.beans.factory.annotation.Autowired;
10
11 import com.cyd.shiro.admin.SysUsersService;
12
13 public class ExtendShiroFilterFactoryBean extends ShiroFilterFactoryBean{
14
15 @Autowired
16 private SysUsersService sysusersservice;
17 //PathMatchingFilter
18 @Override
19 public void setFilterChainDefinitions(String definitions) {
20 //数据库中获取权限,{/index.jsp=authc,onrole[“admin2”,”admin”], /abc.jsp=authc,onrole[“admin2”,”admin”]}
21 Map<String, String> otherChains = sysusersservice.getFilterChain();
22 Ini ini = new Ini();
23 ini.load(definitions);
24 Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
25 if (CollectionUtils.isEmpty(section)) {
26 section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
27 }
28 section.putAll(otherChains);
29 setFilterChainDefinitionMap(section);
30 }
31
32 }

复制代码

三、  学习笔记

1、INI文件配置

复制代码

[users] #提供了对用户/密码及其角色的配置,用户名=密码,角色1,角色2

zhang=123,admin

[roles] #提供了角色及权限之间关系的配置,角色=权限1,权限2

admin=index.jsp

[urls] #配置拦截器链,/** 为拦截器链名称(filterChain),authc,roles[admin],perms[“index.jsp”]拦截器列表名

/login.jsp=anon

/loginController=anon

/unauthorized.jsp=anon

/**=authc,roles[admin],perms[“index.jsp”]

复制代码

2、拦截器链

  Shiro的所有拦截器链名定义在源码DefaultFilter中。

anon            

例子/admins/**=anon 没有参数,表示可以匿名使用。 

authc

例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数  

roles

 例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,  

 并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles[“admin,guest”],  

 每个参数通过才算通过,相当于hasAllRoles()方法。  

perms

例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,  

例如/admins/user/**=perms[“user:add:*,user:modify:*“],当有多个参数时必须每个参数都通过才通过,  

想当于isPermitedAll()方法。

rest

例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,  

 其中method为post,get,delete等。

port

例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,  其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。

authcBasic                                 

例如/admins/user/**=authcBasic没有参数表示httpBasic认证

ssl

例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

user

例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查  

注:anon,authcBasic,auchc,user是认证过滤器,

perms,roles,ssl,rest,port是授权过滤器

3、拦截器链源码类关系图

①   NameableFilter有一个name属性,定义每一个filter的名字。

②   OncePerRequestFilter保证客户端请求后该filter的doFilter只会执行一次。

  doFilterInternal非常重要,在shiro整个filter体系中的核心方法及实质入口。另外,shiro是通过在request中设置一个该filter特定的属性值来保证该filter只会执行一次的。

③   AdviceFilter中主要是对doFilterInternal做了更细致的切分。

  springmvc中的Interceptor,doFilterInternal会先调用preHandle做一些前置判断,如果返回false则filter链不继续往下执行,

④   AccessControlFilter中的对onPreHandle方法做了进一步细化。

  isAccessAllowed方法和onAccessDenied方法达到控制效果。这两个方法都是抽象方法,由子类去实现。到这一层应该明白。isAccessAllowed和onAccessDenied方法会影响到onPreHandle方法,而onPreHandle方法会影响到preHandle方法,而preHandle方法会达到控制filter链是否执行下去的效果。所以如果正在执行的filter中isAccessAllowed和onAccessDenied都返回false,则整个filter控制链都将结束,不会到达目标方法(客户端请求的接口),而是直接跳转到某个页面(由filter定义的,将会在authc中看到)。

⑤   FormAuthenticationFiltershiro提供的登录的filter,

  saveRequestAndRedirectToLogin保存request并拦截到登陆页面,登陆成功后可从WebUtils.getSavedRequest(req);中取出。

四、未实现的功能

  • 动态URL权限控制。当修改权限时,重新加载拦截器链。
  • 密码加密
  • 记住我
  • 在线人数控制
  • 集成验证码

 

五、参考链接

同学们有福了,花了一些时间,重新整理了一个最完整的Mybatis Generator(简称MBG)的最完整配置文件,带详解,再也不用去看EN的User Guide了;

复制代码

<generatorConfiguration>

<context id=“mysql” defaultModelType=“hierarchical” targetRuntime=“MyBatis3Simple” >

<!-- 自动识别数据库关键字,默认false,如果设置为true,根据SqlReservedWords中定义的关键字列表;
    一般保留默认值,遇到数据库关键字(Java关键字),使用columnOverride覆盖 \-->
<property name\="autoDelimitKeywords" value\="false"/>
<!-- 生成的Java文件的编码 \-->
<property name\="javaFileEncoding" value\="UTF-8"/>
<!-- 格式化java代码 \-->
<property name\="javaFormatter" value\="org.mybatis.generator.api.dom.DefaultJavaFormatter"/>
<!-- 格式化XML代码 \-->
<property name\="xmlFormatter" value\="org.mybatis.generator.api.dom.DefaultXmlFormatter"/>

<!-- beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是\`反引号; \-->
<property name\="beginningDelimiter" value\="\`"/>
<property name\="endingDelimiter" value\="\`"/>

<!-- 必须要有的,使用这个配置链接数据库
    @TODO:是否可以扩展 \-->
<jdbcConnection driverClass\="com.mysql.jdbc.Driver" connectionURL\="jdbc:mysql:///pss" userId\="root" password\="admin"\>
    <!-- 这里面可以设置property属性,每一个property属性都设置到配置的Driver上 \-->
</jdbcConnection\>

<!-- java类型处理器 
    用于处理DB中的类型到Java中的类型,默认使用JavaTypeResolverDefaultImpl;
    注意一点,默认会先尝试使用Integer,Long,Short等来对应DECIMAL和 NUMERIC数据类型; \-->
<javaTypeResolver type\="org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl"\>
    <!-- true:使用BigDecimal对应DECIMAL和 NUMERIC数据类型
        false:默认,
            scale>0;length>18:使用BigDecimal;
            scale=0;length\[10,18\]:使用Long;
            scale=0;length\[5,9\]:使用Integer;
            scale=0;length<5:使用Short; \-->
    <property name\="forceBigDecimals" value\="false"/>
</javaTypeResolver\>

<!-- java模型创建器,是必须要的元素
    负责:1,key类(见context的defaultModelType);2,java类;3,查询类
    targetPackage:生成的类要放的包,真实的包受enableSubPackages属性控制;
    targetProject:目标项目,指定一个存在的目录下,生成的内容会放到指定目录中,如果目录不存在,MBG不会自动建目录 \-->
<javaModelGenerator targetPackage\="com.\_520it.mybatis.domain" targetProject\="src/main/java"\>
    <!-- for MyBatis3/MyBatis3Simple
        自动为每一个生成的类创建一个构造方法,构造方法包含了所有的field;而不是使用setter; \-->
    <property name\="constructorBased" value\="false"/>

    <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false \-->
    <property name\="enableSubPackages" value\="true"/>

    <!-- for MyBatis3 / MyBatis3Simple
        是否创建一个不可变的类,如果为true,
        那么MBG会创建一个没有setter方法的类,取而代之的是类似constructorBased的类 \-->
    <property name\="immutable" value\="false"/>

    <!-- 设置一个根对象,
        如果设置了这个根对象,那么生成的keyClass或者recordClass会继承这个类;在Table的rootClass属性中可以覆盖该选项
        注意:如果在key class或者record class中有root class相同的属性,MBG就不会重新生成这些属性了,包括:
            1,属性名相同,类型相同,有相同的getter/setter方法; \-->
    <property name\="rootClass" value\="com.\_520it.mybatis.domain.BaseDomain"/>

    <!-- 设置是否在getter方法中,对String类型字段调用trim()方法 \-->
    <property name\="trimStrings" value\="true"/>
</javaModelGenerator\>

<!-- 生成SQL map的XML文件生成器,
    注意,在Mybatis3之后,我们可以使用mapper.xml文件+Mapper接口(或者不用mapper接口),
        或者只使用Mapper接口+Annotation,所以,如果 javaClientGenerator配置中配置了需要生成XML的话,这个元素就必须配置
    targetPackage/targetProject:同javaModelGenerator \-->
<sqlMapGenerator targetPackage\="com.\_520it.mybatis.mapper" targetProject\="src/main/resources"\>
    <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false \-->
    <property name\="enableSubPackages" value\="true"/>
</sqlMapGenerator\>

<!-- 对于mybatis来说,即生成Mapper接口,注意,如果没有配置该元素,那么默认不会生成Mapper接口 
    targetPackage/targetProject:同javaModelGenerator
    type:选择怎么生成mapper接口(在MyBatis3/MyBatis3Simple下):
        1,ANNOTATEDMAPPER:会生成使用Mapper接口+Annotation的方式创建(SQL生成在annotation中),不会生成对应的XML;
        2,MIXEDMAPPER:使用混合配置,会生成Mapper接口,并适当添加合适的Annotation,但是XML会生成在XML中;
        3,XMLMAPPER:会生成Mapper接口,接口完全依赖XML;
    注意,如果context是MyBatis3Simple:只支持ANNOTATEDMAPPER和XMLMAPPER \-->
<javaClientGenerator targetPackage\="com.\_520it.mybatis.mapper" type\="ANNOTATEDMAPPER" targetProject\="src/main/java"\>
    <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false \-->
    <property name\="enableSubPackages" value\="true"/>

    <!-- 可以为所有生成的接口添加一个父接口,但是MBG只负责生成,不负责检查
    <property name="rootInterface" value=""/> \-->
</javaClientGenerator\>

<!-- 选择一个table来生成相关文件,可以有一个或多个table,必须要有table元素
    选择的table会生成一下文件:
    1,SQL map文件
    2,生成一个主键类;
    3,除了BLOB和主键的其他字段的类;
    4,包含BLOB的类;
    5,一个用户生成动态查询的条件类(selectByExample, deleteByExample),可选;
    6,Mapper接口(可选)

    tableName(必要):要生成对象的表名;
    注意:大小写敏感问题。正常情况下,MBG会自动的去识别数据库标识符的大小写敏感度,在一般情况下,MBG会
        根据设置的schema,catalog或tablename去查询数据表,按照下面的流程:
        1,如果schema,catalog或tablename中有空格,那么设置的是什么格式,就精确的使用指定的大小写格式去查询;
        2,否则,如果数据库的标识符使用大写的,那么MBG自动把表名变成大写再查找;
        3,否则,如果数据库的标识符使用小写的,那么MBG自动把表名变成小写再查找;
        4,否则,使用指定的大小写格式查询;
    另外的,如果在创建表的时候,使用的""把数据库对象规定大小写,就算数据库标识符是使用的大写,在这种情况下也会使用给定的大小写来创建表名;
    这个时候,请设置delimitIdentifiers="true"即可保留大小写格式;

    可选:
    1,schema:数据库的schema;
    2,catalog:数据库的catalog;
    3,alias:为数据表设置的别名,如果设置了alias,那么生成的所有的SELECT SQL语句中,列名会变成:alias\_actualColumnName
    4,domainObjectName:生成的domain类的名字,如果不设置,直接使用表名作为domain类的名字;可以设置为somepck.domainName,那么会自动把domainName类再放到somepck包里面;
    5,enableInsert(默认true):指定是否生成insert语句;
    6,enableSelectByPrimaryKey(默认true):指定是否生成按照主键查询对象的语句(就是getById或get);
    7,enableSelectByExample(默认true):MyBatis3Simple为false,指定是否生成动态查询语句;
    8,enableUpdateByPrimaryKey(默认true):指定是否生成按照主键修改对象的语句(即update);
    9,enableDeleteByPrimaryKey(默认true):指定是否生成按照主键删除对象的语句(即delete);
    10,enableDeleteByExample(默认true):MyBatis3Simple为false,指定是否生成动态删除语句;
    11,enableCountByExample(默认true):MyBatis3Simple为false,指定是否生成动态查询总条数语句(用于分页的总条数查询);
    12,enableUpdateByExample(默认true):MyBatis3Simple为false,指定是否生成动态修改语句(只修改对象中不为空的属性);
    13,modelType:参考context元素的defaultModelType,相当于覆盖;
    14,delimitIdentifiers:参考tableName的解释,注意,默认的delimitIdentifiers是双引号,如果类似MYSQL这样的数据库,使用的是\`(反引号,那么还需要设置context的beginningDelimiter和endingDelimiter属性)
    15,delimitAllColumns:设置是否所有生成的SQL中的列名都使用标识符引起来。默认为false,delimitIdentifiers参考context的属性

    注意,table里面很多参数都是对javaModelGenerator,context等元素的默认属性的一个复写; \-->
<table tableName\="userinfo" \>

    <!-- 参考 javaModelGenerator 的 constructorBased属性\-->
    <property name\="constructorBased" value\="false"/>

    <!-- 默认为false,如果设置为true,在生成的SQL中,table名字不会加上catalog或schema; \-->
    <property name\="ignoreQualifiersAtRuntime" value\="false"/>

    <!-- 参考 javaModelGenerator 的 immutable 属性 \-->
    <property name\="immutable" value\="false"/>

    <!-- 指定是否只生成domain类,如果设置为true,只生成domain类,如果还配置了sqlMapGenerator,那么在mapper XML文件中,只生成resultMap元素 \-->
    <property name\="modelOnly" value\="false"/>

    <!-- 参考 javaModelGenerator 的 rootClass 属性 
    <property name="rootClass" value=""/> \-->

    <!-- 参考javaClientGenerator 的  rootInterface 属性
    <property name="rootInterface" value=""/> \-->

    <!-- 如果设置了runtimeCatalog,那么在生成的SQL中,使用该指定的catalog,而不是table元素上的catalog 
    <property name="runtimeCatalog" value=""/> \-->

    <!-- 如果设置了runtimeSchema,那么在生成的SQL中,使用该指定的schema,而不是table元素上的schema 
    <property name="runtimeSchema" value=""/> \-->

    <!-- 如果设置了runtimeTableName,那么在生成的SQL中,使用该指定的tablename,而不是table元素上的tablename 
    <property name="runtimeTableName" value=""/> \-->

    <!-- 注意,该属性只针对MyBatis3Simple有用;
        如果选择的runtime是MyBatis3Simple,那么会生成一个SelectAll方法,如果指定了selectAllOrderByClause,那么会在该SQL中添加指定的这个order条件; \-->
    <property name\="selectAllOrderByClause" value\="age desc,username asc"/>

    <!-- 如果设置为true,生成的model类会直接使用column本身的名字,而不会再使用驼峰命名方法,比如BORN\_DATE,生成的属性名字就是BORN\_DATE,而不会是bornDate \-->
    <property name\="useActualColumnNames" value\="false"/>

    <!-- generatedKey用于生成生成主键的方法,
        如果设置了该元素,MBG会在生成的<insert>元素中生成一条正确的<selectKey>元素,该元素可选
        column:主键的列名;
        sqlStatement:要生成的selectKey语句,有以下可选项:
            Cloudscape:相当于selectKey的SQL为: VALUES IDENTITY\_VAL\_LOCAL()
            DB2       :相当于selectKey的SQL为: VALUES IDENTITY\_VAL\_LOCAL()
            DB2\_MF    :相当于selectKey的SQL为:SELECT IDENTITY\_VAL\_LOCAL() FROM SYSIBM.SYSDUMMY1
            Derby      :相当于selectKey的SQL为:VALUES IDENTITY\_VAL\_LOCAL()
            HSQLDB      :相当于selectKey的SQL为:CALL IDENTITY()
            Informix  :相当于selectKey的SQL为:select dbinfo('sqlca.sqlerrd1') from systables where tabid=1
            MySql      :相当于selectKey的SQL为:SELECT LAST\_INSERT\_ID()
            SqlServer :相当于selectKey的SQL为:SELECT SCOPE\_IDENTITY()
            SYBASE      :相当于selectKey的SQL为:SELECT @@IDENTITY
            JDBC      :相当于在生成的insert元素上添加useGeneratedKeys="true"和keyProperty属性
    <generatedKey column="" sqlStatement=""/> \-->

    <!-- 该元素会在根据表中列名计算对象属性名之前先重命名列名,非常适合用于表中的列都有公用的前缀字符串的时候,
        比如列名为:CUST\_ID,CUST\_NAME,CUST\_EMAIL,CUST\_ADDRESS等;
        那么就可以设置searchString为"^CUST\_",并使用空白替换,那么生成的Customer对象中的属性名称就不是
        custId,custName等,而是先被替换为ID,NAME,EMAIL,然后变成属性:id,name,email;

        注意,MBG是使用java.util.regex.Matcher.replaceAll来替换searchString和replaceString的,
        如果使用了columnOverride元素,该属性无效;

    <columnRenamingRule searchString="" replaceString=""/> \-->

     <!-- 用来修改表中某个列的属性,MBG会使用修改后的列来生成domain的属性;
         column:要重新设置的列名;
         注意,一个table元素中可以有多个columnOverride元素哈~ \-->
     <columnOverride column\="username"\>
         <!-- 使用property属性来指定列要生成的属性名称 \-->
         <property name\="property" value\="userName"/>

         <!-- javaType用于指定生成的domain的属性类型,使用类型的全限定名
         <property name="javaType" value=""/> \-->

         <!-- jdbcType用于指定该列的JDBC类型 
         <property name="jdbcType" value=""/> \-->

         <!-- typeHandler 用于指定该列使用到的TypeHandler,如果要指定,配置类型处理器的全限定名
             注意,mybatis中,不会生成到mybatis-config.xml中的typeHandler
             只会生成类似:where id = #{id,jdbcType=BIGINT,typeHandler=com.\_520it.mybatis.MyTypeHandler}的参数描述
         <property name="jdbcType" value=""/> \-->

         <!-- 参考table元素的delimitAllColumns配置,默认为false
         <property name="delimitedColumnName" value=""/> \-->
     </columnOverride\>

     <!-- ignoreColumn设置一个MGB忽略的列,如果设置了改列,那么在生成的domain中,生成的SQL中,都不会有该列出现 
         column:指定要忽略的列的名字;
         delimitedColumnName:参考table元素的delimitAllColumns配置,默认为false

         注意,一个table元素中可以有多个ignoreColumn元素
     <ignoreColumn column="deptId" delimitedColumnName=""/> \-->
</table\>

</context>

</generatorConfiguration>

复制代码