精华内容
下载资源
问答
  • 二维码展示界面高亮设置,仿支付宝收款码,付款码界面高亮实现
    万次阅读
    2017-05-03 10:27:55

    我们有时后会遇到这样一个场景,当进入某一个界面的时候需要这个界面高亮显示,而其他界面则是正常的亮度。比如说,在使用支付宝收付款时,进入二维码展示界面时,页面会变高亮。

    现在来看看具体的解决方案。

    一. 首先 UIScreen 有一个属性 brightness 可以设置屏幕的亮度。值为 0 ~ 1之间,为1时亮度最高,0 时亮度最低。

    二.我们需要定义几个宏,在pch文件或者其他文件中。

    #define SystemBrightness    @"systemBrightness" //用于记录系统亮度
    
    #define Notification_SetBrightnessSystem  @"Notification_SetBrightnessSystem"   //设置屏幕为系统亮度的通知
    #define Notification_SetBrightnessHighLight  @"Notification_SetBrightnessHighLight"   //设置屏幕高亮的通知
    

    三. 我们需要高亮显示的那个页面 .m

    #import "QRCodeController.h"
    @interface QRCodeController ()
    @end
    @implementation QRCodeController
    
    - (void)dealloc
    {
        //移除通知
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        //首先记录下当前系统的屏幕亮度,保存到偏好设置
        [[NSUserDefaults standardUserDefaults] setFloat:[UIScreen mainScreen].brightness forKey:SystemBrightness];
        [[NSUserDefaults standardUserDefaults] synchronize];
    
        //添加设置屏幕亮度的两个通知,主要是为了解决应用间跳转,应用从此界面进入后台,或从后台直接进入此界面的一些问题
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setBrightnessSystem) name:Notification_SetBrightnessSystem object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setBrightnessHighLight) name:Notification_SetBrightnessHighLight object:nil];
    }
    
    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        //在这个控制器出现的时候,将屏幕设置为高亮
        [self setBrightnessHighLight];
    }
    
    - (void)viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
        //在这个控制器将要消失的时候,将屏幕设置为原系统亮度
        [self setBrightnessSystem];
    }
    
    //将屏幕设置为原系统亮度
    - (void)setBrightnessSystem
    {
        [UIScreen mainScreen].brightness = [[NSUserDefaults standardUserDefaults] floatForKey:SystemBrightness];
    }
    //将屏幕设置为高亮
    - (void)setBrightnessHighLight
    {
        //具体亮度可以根据自己的需求,自定义设置亮度值
        [UIScreen mainScreen].brightness = 1;
    }
    

    四. 在AppDelegate监听APP的状态,适时发出通知,更改屏幕亮度

    //APP将要进入后台,跳转到其他应用,双击home键,下拉通知栏等情况下会调用
    - (void)applicationWillResignActive:(UIApplication *)application 
    {
        //APP进入非活跃状态时设置发送设置屏幕为系统亮度通知
        [[NSNotificationCenter defaultCenter] postNotificationName:Notification_SetBrightnessSystem object:nil];
    }
    
    - (void)applicationDidBecomeActive:(UIApplication *)application 
    {
        //APP进入活跃状态时发送设置屏幕高亮通知
        [[NSNotificationCenter defaultCenter] postNotificationName:Notification_SetBrightnessHighLight object:nil];
    }

    按这几步走,就完美解决了设置某一界面高亮的问题了。

    如有问题,欢迎评论交流!

    更多相关内容
  • 交易的时候需要商家的收银系统上选择银行,然后通过第三方支付机构跳转到对应的银行网银完成支付流程。 图片来自网络 网关支付一般是PC端的IE浏览器进行操作,移动端也有些机构提供,但是似乎兼容性一直不...


    从事支付行业开发多年,做过很多不同的场景。发现各种支付方式多样化,还有各种不同的叫法,很多人都是一知半解,容易混淆一些概念。这篇文章希望根据自己的理解,尽量的把几种支付方式说清楚。

    线上和线下

    首先从大类上,任意一种支付方式必定属于这两类。线上和线下的区别在于商品交易的场景是在实体的店铺还是互联网上(比如电商平台)。

    线下支付场景分类

    付款码支付

    也有叫条码支付的,也有叫被扫(从用户的角度)。**其实名字不重要,关键看场景。**它的场景是这样的:

    商家使用扫码枪等条码识别设备扫描用户APP上的条码(一维码或者二维码),完成收款。用户仅需出示付款码,所有收款操作由商家端完成。支付宝的示例如下图:

    image
    图片来自网络

    具体步骤是:

    1. 用户打开支付APP(支付宝,微信或者云闪付等),找到付款码界面;
    2. 收银员在商家收银系统操作生成订单,用户确认支付金额;
    3. 收银员使用扫码设备(包括扫码枪,POS机等),扫描用户手机上的条码(一维码或者二维码),商家收银系统提交支付。
    4. 机构后台(支付宝,微信支付,银联等)收到支付请求在后台进行交易处理。
    5. 付款成功(或者失败)后商家的收银系统和用户都会有相关的提示。

    在第三步,支付机构会提供支付的接口,接口提供的方式有多种,比如有SDK的方式,还有HTTP的方式。请求的报文里携带付款码的信息(是一串数字,不同的支付机构特征不一样)。

    这里还是拿支付宝的条码支付举个栗子:

    https://openapi.alipay.com/gateway.do?timestamp=2013-01-01 08:08:08&method=alipay.trade.pay&app_id=2284&sign_type=RSA2&sign=ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE&version=1.0&biz_content=
      {
        "out_trade_no":"20150320010101001",
        "scene":"bar_code,wave_code",
        "auth_code":"28763443825664394",
        "subject":"Iphone6 16G",
        "seller_id":"2088102146225135",
        "total_amount":"88.88",
        "store_id":"NJ_001"
      }
    

    其中auth_code就是付款码的信息。

    这种场景和传统的POS收款其实异曲同工,本人早年从事过POS机支付相关的开发工作,POS机线下收款获取的是银行的信息,而条码支付获取的是用户APP上的付款码信息。获取信息之后都是往第三方付支付机构(包括银行,支付宝,微信等)发起支付请求并最终完成整个支付流程。

    图片来自网络
    图片来自网络

    扫码支付

    扫码支付也是一种线下的支付方式。它和付款码的区别扫码的主体互换了。是由用户使用APP扫描商户收银端生成的二维码。支付宝的示例如下图:

    image
    图片来自网络

    具体步骤是:

    1. 商家的收银系统根据用户购买的商品生成订单信息,并根据订单信息并生成二维码;
    2. 用户打开APP的扫一扫界面,扫描第一步的二维码,核对金额然后支付;
    3. 用户付款后商家收银系统会拿到支付成功或者失败的结果。同时用户本身也会收到相应的结果。

    扫码支付的场景多见于自动售货机。大家仔细回想下在自动售货机上购买商品的过程就明白了。

    [外链图片转存失败(img-VfFd9NG4-1567745094317)(http://img.dav01.com/e/2018/6/21/davinfo_215546_1529562031320_1555442136.png)]

    图片来自网络

    线上支付场景

    APP支付

    场景是商家在APP上集成支付宝或者微信支付的功能。比如我们使用美团外面APP订外卖,付款的时候会弹出微信APP进行支付,这就是APP支付的场景。

    示例如下图:

    image
    图片来自网络

    步骤如下:

    1. 用户进入商户APP,选择商品下单、确认购买,进入支付环节。商户服务后台生成支付订单数据,然后由商户APP调用支付SDK的接口;
    2. 支付APP这个时候会被调用起来进行输入密码等操作并完成支付;

    APP支付一般是提供手机端的封装好的SDK给到商户的APP调用,所以有安卓和IOS两个版本,这是和HTTP接口不同的地方。这里拿支付宝的安卓端SDK举个栗子(这里只展示关键部分的代码):

    
    	public void payV2(View v) {
    		...
    		final Runnable payRunnable = new Runnable() {
    
    			@Override
    			public void run() {
    				PayTask alipay = new PayTask(PayDemoActivity.this);
    				Map<String, String> result = alipay.payV2(orderInfo, true);
    				Log.i("msp", result.toString());
    				
    				Message msg = new Message();
    				msg.what = SDK_PAY_FLAG;
    				msg.obj = result;
    				mHandler.sendMessage(msg);
    			}
    		};
    
    		...
    	}
    

    PayTask就是SDK提供的用于发起支付的核心类,通过调用它的方法payV2发起支付请求。payV2的第一个参数就是封装的订单信息对象。

    公众号支付

    微信叫公众号支付,支付宝叫生活号支付(也有叫支付宝服务窗支付的)。其实针对的场景是相似的,微信针对的是公众号里面的商家H5页面发起支付的场景,而支付宝针对的是生活号里的商家H5页面发起的支付场景。

    这个拿大家熟悉的微信公众号举个栗子,具体的流程如下:

    1. 进入公众号自定义菜单,用户点击进入商户网页选择购买商品;
    2. 调起微信支付控件,用户输入支付密码完成支付;
    3. 商户后台得到支付成功(或者失败)的通知;

    image
    image

    image
    image

    图片来自网络

    这里有几个关键的点说明以下:

    • 支付过程并不会唤起微信APP,而是启动了一个微信支付的控件
    • 支付请求参数中需要携带用户的openid,这个也是由公众号支付的特殊性决定的,在微信内置浏览器中是可以获取到用户的openid的;

    H5支付和手机WAP支付

    这两个其实是一回事,只是叫法不同。场景都是在普通的浏览器(非支付宝和微信内置浏览器)打开商户的H5页面发起支付,唤起支付APP进行支付的情况。支付完后跳回到商家H5页面内,最后展示支付结果。

    image
    image
    image

    图片来自网络

    据我的了解,这种场景应该是目前使用的最广泛的。比如我小区的电梯里经常看到一些蛋糕的广告,扫描上面的二维码就进入了商家的H5页面,然后选择商品购买即可,非常方便!

    小程序支付

    微信和支付宝现在都再力推小程序,那怎么能少得了支付的支持呢!

    小程序的支付这里不详述了,因为它和公众号支付基本是一样的。就是在配置上有些差异,比如微信小程序支付不需要配置支付目录,授权域名这些了,简化了一些流程。

    网关支付

    网关支付也是属于线上支付,但它又不同于前面介绍的互联网支付方式。它属于传统的银行网银支付。一般情况下是符合资质的第三方支付机构提供的一种支付服务。

    这些机构后台会接入多家银行,然后在前台放出接口给到商家。交易的时候需要在商家的收银系统上选择银行,然后通过第三方支付机构跳转到对应的银行网银完成支付流程。

    image

    图片来自网络

    网关支付一般是在PC端的IE浏览器进行操作,移动端也有些机构提供,但是似乎兼容性一直不太好。场景一般是有大额往来的B2B业务。

    在这里插入图片描述
    欢迎大家关注我的公众号

    展开全文
  • 文章目录一、Shiro概述1.1、权限管理1.1.1、权限管理1.1.2、身份认证1.1.3、授权2.1、什么是shiro2.2、为什么要学shiro2.3、基本功能2.4、架构说明2.5、下载Shiro和maven的依赖二、Shiro基础入门2.1、Shiro.ini文件...

    文章目录

    一、Shiro概述

    1.1、权限管理

    1.1.1、权限管理

        基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。

        权限管理包括用户身份认证授权两部分,简称认证授权。对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。

    1.1.2、身份认证

        身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡。

    1.1.3、授权

        授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的

    2.1、什么是shiro

    image-20200520220106539
        Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.

        Apache Shiro 是Java 的一个安全框架。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与Web 集成、缓存等。

    2.2、为什么要学shiro

    1. 既然shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。
    2. shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。

        java领域中spring security(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro。

    2.3、基本功能

    image-20200927182532297

    1. Authentication

    身份认证/登录,验证用户是不是拥有相应的身份;

    1. Authorization

    授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用 户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用 户对某个资源是否具有某个权限;

    1. SessionManager

    会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信 息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

    1. Cryptography

    加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

    1. WebSupport

    Web 支持,可以非常容易的集成到Web 环境;

    1. Caching

    缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

    1. Concurrency

    shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能 把权限自动传播过去;

    1. Testing

    提供测试支持;

    1. Run As

    允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

    1. Remember Me

    记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录 了。

        注意:Shiro 不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。关于设计,后面的ssm集成shiro里面去说哦

    2.4、架构说明

    image-20200927210420029

    1. Subject

    Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

    1. SecurityManager

    SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。

    SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。

    1. Authenticator

    Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。

    1. Authorizer

    Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

    1. Realm

    Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。

    **注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。 **

    1. sessionManager

    sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

    1. SessionDAO

    SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

    1. CacheManager

    CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

    1. Cryptography

    Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。

    2.5、下载Shiro和maven的依赖

    http://shiro.apache.org/download.html

    image-20200928092649234

    image-20200928092719767

    • shiro-all 是shiro的所有功能jar包
    • shiro-core 是shiro的基本功能包
    • shiro-web 和web集成的包
    • shiro-spring shrio和spring集成的包

    二、Shiro基础入门

    2.1、Shiro.ini文件的说明

    1. ini (InitializationFile) 初始文件.Window系统文件扩展名.

    2. Shiro 使用时可以连接数据库,也可以不连接数据库. 如果不连接数据库,可以在shiro.ini中配置静态数据

    2.2、Shrio.ini文件的组成部分

    [main] :定义全局变量

    1. 内置securityManager对象.

    2. 操作内置对象时,在[main]里面写东西

    [main]
    securityManager.属性=值
    xiaolin=123456
    securityManager.对象属性=$xiaolin
    
    

    [users] :定义用户名和密码

    [users]
    # 定义用户名为zhangsan 密码为zs
    zhangsan=zs
    # 定义用户名lisi密码为lisi同时具有role1和role2两个角色
    lisi=lisi,role1,role2
    
    

    [roles]:定义角色

    [roles]
    role1=权限名1,权限名2 
    role2=权限3,权限4
    
    

    [urls] :定义哪些内置urls生效.在web应用时使用.

    [urls]
    #url地址=内置filter或自定义filter
    # 访问时出现/login的url必须去认证.支持authc对应的Filter 
    /login=authc
    # 任意的url都不需要进行认证等功能.
    /** = anon
    # 所有的内容都必须保证用户已经登录.
    /**=user
    # url abc 访问时必须保证用户具有role1和role2角色.
    /abc=roles[“role1,role2”]
    
    

    2.3、过滤器详解

    过滤器名称过滤器类描述
    anonorg.apache.shiro.web.filter.authc.AnonymousFilter匿名过滤器
    authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter如果继续操作,需要做对应的表单验证否则不能通过
    authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter基本http验证过滤,如果不通过,跳转屋登录页面
    logoutorg.apache.shiro.web.filter.authc.LogoutFilter登录退出过滤器
    noSessionCreationorg.apache.shiro.web.filter.session.NoSessionCreationFilter没有session创建过滤器
    permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter权限过滤器
    portorg.apache.shiro.web.filter.authz.PortFilter端口过滤器,可以设置是否是指定端口如果不是跳转到登录页面
    restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilterhttp方法过滤器,可以指定如post不能进行访问等
    rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter角色过滤器,判断当前用户是否指定角色
    sslorg.apache.shiro.web.filter.authz.SslFilter请求需要通过ssl,如果不是跳转回登录页
    userorg.apache.shiro.web.filter.authc.UserFilter如果访问一个已知用户,比如记住我功能,走这个过滤器

    anon: 匿名处理过滤器,即不需要登录即可访问;一般用于静态资源过滤;

    如: /static/**=anon

    authc: 表示需要认证(登录)才能使用;

    如:/**=authc

    roles:角色授权过滤器,验证用户是否拥有资源角色;

    如:/admin/*=roles[admin]

    perms:权限授权过滤器,验证用户是否拥有资源权限;

    如:/employee/input=perms[“user:update”]

    logout:注销过滤器

    如: /logout=logout

    三、认证

    3.1、认证

        认证的过程即为用户的身份确认过程,所实现的功能就是我们所熟悉的登录验证,用户输入账号和密码提交到后台,后台通过访问数据库执行账号密码的正确性校验。

    3.2、认证中的关键对象

    3.2.1、Subject:主体

        访问系统的用户,主体可以是用户、程序等,进行认证的都称为体;

    3.2.2、Principal:身份信息

        是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)。

    3.2.3、credential:凭证信息

        是只有主体自己知道的安全信息,如密码、证书等。

    3.3、认证流程

    image-20200521204452288

    image-20200707152052684

        最终执行用户比较是在 SimpleAccountRealm类中的doGetAuthenticationInfo方法完成了用户名的校验。

        最终密码校验是在 AuthenticatingRealm类中的assertCredentialsMatch方法中完成密码的校验,且是自动的,不需要我们手动完成。

    3.4、代码实现

    3.4.1、引入依赖

    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.5.2</version>
    </dependency>
    
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.22</version>
        <scope>provided</scope>
    </dependency>
    

    3.4.2、编写ini文件

    [users]
    xiaolin=666
    zs=13
    

    3.4.3、编写测试类

     /**
      * 用于测试Shiro基础
      */
      @Test
      public void testShiroBase(){
        // 创建Shiro安全管理器,是Shiro的核心
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        // 加载ini配置文件,得到配置文件中配置的用户信息
        IniRealm iniRealm = new IniRealm("classpath:shiro-authc.ini");
        // 将realm对象注入到securityManager中
        securityManager.setRealm(iniRealm);
        // 将安全管理器注入到当前环境中
        SecurityUtils.setSecurityManager(securityManager);
        // 获取subject主体对象,无论是否登录都可以获取
        Subject subject = SecurityUtils.getSubject();
        // 打印一下认证状态
        System.out.println("认证状态"+subject.isAuthenticated());
        // 创建一个携带了账号和密码的令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaolin", "666");
        // 调用subject传入令牌进行登录
        subject.login(token);
        System.out.println("认证状态"+subject.isAuthenticated());
      }
    

    3.4.4、注意

        如果输入的身份和凭证和 ini 文件中配置的能够匹配,那么登录成功,登录状态为true,反之登录状态为false。

        登录失败一般存在几种情况(抛出的异常):

    1. 账号错误:org.apache.shiro.authc.UnknownAccountException
    2. 密码错误:org.apache.shiro.authc.IncorrectCredentialsException

    3.4.5、Shiro认证源码分析

    1、调用subject.login方法进行登录,其会自动委托给securityManager.login方法进行登录;
    2、securityManager通过Authenticator(认证器)进行认证;
    3、Authenticator的实现类ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码,这里使用的是IniRealm(shiro自带,相当于数据源);
    4、IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。

    image-20200707152208225

    3.5、自定义Realm

        自定义 Realm 在实际开发中使用非常多,应该我们需要使用的账户信息通常来自程序或者数据库中, 而不是前面使用到的 ini 文件的配置。

        在 AuthenticatingRealm 中调用doGetAuthenticationInfo方法来获取,如果返回的 info不等于空,说明账号存在,才会进行密码校验,如果不存在则直接抛出UnknownAccountException异常。

        所以,如果我们要自定义 Realm,应该覆写 doGetAuthenticationInfo()方法,然后在该方法中实现账号的校验,并返回 AuthenticationInfo 对象给上层调用者 AuthenticatingRealm 做进一步的校验。

    3.5.1、Shiro提供的Realm

    image-20200521212728541

    3.5.2、SimpleAccountRealm

        我们进行Debug的时候,可以看到认证的源码中使用的是SimpleAccountRealm。

    image-20200521213451998

    3.5.3、SimpleAccountRealm的部分源码

    // 认证
    public class SimpleAccountRealm extends AuthorizingRealm {
    		//.......省略
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            UsernamePasswordToken upToken = (UsernamePasswordToken) token;
            SimpleAccount account = getUser(upToken.getUsername());
    
            if (account != null) {
    
                if (account.isLocked()) {
                    throw new LockedAccountException("Account [" + account + "] is locked.");
                }
                if (account.isCredentialsExpired()) {
                    String msg = "The credentials for account [" + account + "] are expired";
                    throw new ExpiredCredentialsException(msg);
                }
    
            }
    
            return account;
        }
    
        // 授权
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            String username = getUsername(principals);
            USERS_LOCK.readLock().lock();
            try {
                return this.users.get(username);
            } finally {
                USERS_LOCK.readLock().unlock();
            }
        }
    }
    

    3.6、SSM整合Shiro认证

    3.6.1、添加依赖

    <properties>
    	<shiro.version>1.5.2</shiro.version>
    </properties>
    <!--shiro 核心-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>${shiro.version}</version>
    </dependency>
    <!--shiro 的 Web 模块-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-web</artifactId>
        <version>${shiro.version}</version>
    </dependency>
    <!--shiro 和 Spring 集成-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>${shiro.version}</version>
    </dependency>
    <!--shiro 底层使用的 ehcache 缓存-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-ehcache</artifactId>
        <version>${shiro.version}</version>
    </dependency>
    <!--shiro 依赖的日志包-->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <!--Freemarker 的 shiro 标签库-->
    <dependency>
        <groupId>net.mingsoft</groupId>
        <artifactId>shiro-freemarker-tags</artifactId>
        <version>1.0.1</version>
    </dependency>
    

    3.6.2、配置代理过滤器

        在访问的时候,需要做一系列的预处理操作,Shiro是选择使用filter过滤器来进行拦截的,因为Shiro不依赖Spring容器,所以当没有springmvc时意味着不能用拦截器,但过滤器则不同,只要是web项目都可以使用。我们需要在web.xml进行配置过滤器。

    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>
            org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

        这里使用了一个代理过滤器DelegatingFilterProxy,因为真正的shiroFilter需要注入很多复杂的对象,而web.xml中只能配置字符串或数字的参数,是不能满足的,因此我们会把shiroFilter交给 Spring 进行管理,通过spring的xml文件来配置。 使用DelegatingFilterProxy代理过滤器后,但浏览器发送请求过来,被代理过滤器拦截到后,代理过滤器会自动从 spring 容器中找filter-name所配置相同名称的bean,来实现真正的业务。

    3.6.3、创建shiro.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--自定义Realm-->
      <bean id="myRealm" class="cn.linstudy.shiro.CarBusinessRealm"/>
        <!--创建安全管理器-->
      <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
      </bean>
      <bean id="shiroFilter"
        class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--引用指定的安全管理器-->
        <property name="securityManager" ref="securityManager"/>
        <!--shiro默认的登录地址是/login.jsp 现在要指定我们自己的登录页面地址-->
        <property name="loginUrl" value="/login.html"/>
        <!--路径对应的规则-->
        <property name="filterChainDefinitions">
          <!--配置请求拦截的方式,是可以匿名还是-->
          <value>
            /empLogin=anon
            /static/**=anon
            /upload/**=anon
            /getImage=anon
            /getEmailCode=anon
            /checkEmail=anon
            /checkUsername=anon
            /error=anon
            /fronted/**=anon
            /**=authc
          </value>
        </property>
      </bean>
    </beans>
    

    3.6.4、引入shrio.xml

        在mvc.xml 中引入shiro.xml

    <import resource="classpath:shiro.xml"/>
    

    3.6.5、配置安全管理器

        我们需要在shiro.xml中配置安全管理器。

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"></bean>
    

    3.6.6、配置自定义Realm

    public class CarBusinessRealm extends AuthorizingRealm {  
    @Autowired
      private EmployeeService employeeService;
    
      // 认证
      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
          throws AuthenticationException {
        // 通过token获取用户名(用户登录的时候)
        String username = (String) token.getPrincipal();
        // 判断是否存在在数据库
        Employee employee = employeeService.selectByUsername(username);
        if (employee != null) {
          // 说明此时用户名是对的,返回一个身份对象
          return new SimpleAuthenticationInfo(employee, employee.getPassword(), this.getName());
        }
        return null;
      }
    }
    

    3.6.7、配置自定义Realm

    <bean id="myRealm" class="cn.linstudy.shiro.CarBusinessRealm"/>
      <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
      </bean>	
    

    3.6.8、修改登录方法

          try {
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            SecurityUtils.getSubject().login(token);
            Employee employee = employeeMapper.selectByUsername(username);
            return employee;
          } catch (UnknownAccountException e) {
            throw new CarBussinessException("用户名错误");
          } catch (IncorrectCredentialsException e) {
            throw new CarBussinessException("密码错误");
          }
    

    3.7、登出

        shiro中内置了登出方法,我们只需在shiro.xml中的路径规则加入 /logout=logout 即可交给shiro来处理。

    四、授权

    4.1、授权概述

        授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。
        系统中的授权功能就是为用户分配相关的权限,只有当用户拥有相应的权限后,才能访问对应的资源。
        如果系统中无法管理用户的权限,那么将会出现客户信息泄露,数据被恶意篡改等问题,所以在绝大多数的应用中,我们都会有权限管理模块。一般基于角色的权限控制管理有以下三个子模块:

    1. 用户管理
    2. 角色管理
    3. 权限管理

    授权流程

    4.2、 关键对象

        授权可简单理解为who对what(which)进行How操作:

        Who,即主体(Subject),主体需要访问系统中的资源。

        What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型资源实例,比如商品信息为资源类型,类型为t01的商品为资源实例,编号为001的商品信息也属于资源实例。

        How,权限/许可(Permission),规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。

    4.3、授权流程

    image-20200521230705964

    4.4、授权方式

    4.4.1、基于角色的访问控制

        RBAC基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制

    if(subject.hasRole("admin")){//主体具有admin角色
       //操作什么资源
    }
    

    4.4.2、基于资源的访问控制

        RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制

    if(subject.isPermission("user:find:*")){ //对用户模块的所有用户有查询权限
      //对01用户进行修改
    }
    if(subject.isPermission("user:*:01")){  //user模块下的所有用户有所有权限
      //对01用户进行修改
    }
    

    4.5、权限字符串

    ​ 权限字符串的规则是:资源标识符:操作:资源实例标识符,意思是对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用*通配符。

    例子:

    • 用户创建权限:user:create,或user:create:*
    • 用户修改实例001的权限:user:update:001
    • 用户实例001的所有权限:user:*:001

    4.6、Shiro授权的实现方式

    4.6.1、编程式

    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(“admin”)) {
    	//有权限
    } else {
    	//无权限
    }
    

    4.6.2、注解式

    @RequiresRoles("admin")
    public void hello() {
    	//有权限
    }
    

    4.6.3、标签式

    JSP标签:在JSP 页面通过相应的标签完成:
    <shiro:hasRole name="admin">
    	<!— 有权限—>
    </shiro:hasRole>
    注意: Thymeleaf/FreeMarker 中使用shiro需要额外集成!
    

    4.7、基于ini的授权

    4.7.1、编写ini文件

    #用户的身份、凭据、角色
    [users]
    zhangsan=555,hr,seller
    xiaolin=123,hr
    
    #角色与权限信息
    [roles]
    hr=employee:list,employee:delete
    seller=customer:list,customer:save
    

    4.7.2、编写测试类

    @Test
    public void testAuthor(){
        //创建Shiro的安全管理器,是shiro的核心
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        //加载shiro.ini配置,得到配置中的用户信息(账号+密码)
        IniRealm iniRealm = new IniRealm("classpath:shiro-author.ini");
        securityManager.setRealm(iniRealm);
        //把安全管理器注入到当前的环境中
        SecurityUtils.setSecurityManager(securityManager);
        //无论有无登录都可以获取到subject主体对象,但是判断登录状态需要利用里面的属性来判断
        Subject subject = SecurityUtils.getSubject();
        System.out.println("认证状态:"+subject.isAuthenticated());
        //创建令牌(携带登录用户的账号和密码)
        UsernamePasswordToken token = new UsernamePasswordToken("dafei","666");
        //执行登录操作(将用户的和 ini 配置中的账号密码做匹配)
        subject.login(token);
        System.out.println("认证状态:"+subject.isAuthenticated());
        //登出
        //subject.logout();
        //System.out.println("认证状态:"+subject.isAuthenticated());
        
        //判断用户是否有某个角色
        System.out.println("role1:"+subject.hasRole("role1"));
        System.out.println("role2:"+subject.hasRole("role2"));
    
        //是否同时拥有多个角色
        System.out.println("是否同时拥有role1和role2:"+subject.hasAllRoles(Arrays.asList("role1", "role2")));
    
        //check开头的是没有返回值的,当没有权限时就会抛出异常
        subject.checkRole("hr");
    
        //判断用户是否有某个权限
        System.out.println("user:delete:"+subject.isPermitted("user:delete"));
        subject.checkPermission("user:delete");
    }
    

    4.7.3、自定义Realm

    public class EmployeeRealm extends AuthorizingRealm {
        //提供认证信息
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) 
            throws AuthenticationException {
            //暂且使用假数据来模拟数据库中真实的账号和密码
            Employee employee = new Employee();
            employee.setName("admin");
            employee.setPassword("1");
            //获取token中需要登录的账号名
            String username = (String)token.getPrincipal();
            //如果账号存在,则返回一个 AuthenticationInfo 对象
            if(username.equals(employee.getName())){
                return new SimpleAuthenticationInfo(
                    employee,//身份对象,与主体subject绑定在一起的对象,暂时无用但后续要用 
                    employee.getPassword(),//该账号真正的密码,传给shiro做密码校验的
                    this.getName()//当前 Realm 的名称,暂时无用,不需纠结
                );
            }
            return null;
        }
    	//提供授权信息
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            //登录成功用户对象
            Employee employee = (Employee)principals.getPrimaryPrincipal();
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            // 根据登录用户的id查询到其拥有的所有角色的编码,这里进行模拟数据库
            List<String> roleSns = Arrays.asList("hr","manager","seller");
            // 将用户拥有的角色添加到授权信息对象中,供 Shiro 权限校验时使用
            info.addRoles(roleSns);
            // 根据登录用户的 id 查询到其拥有的所有权限表达式,这里进行模拟数据库
            List<String> expressions = Arrays.asList("employee:list","employee:save");
            // 将用户拥有的权限添加到授权信息对象中,供 Shiro 权限校验时使用
            info.addStringPermissions(expressions);
            return info;
        }
    }
    

    4.8、SSM整合Shiro认证

        在开发中,我们一般使用注解来完成授权操作, 我们需要使用 Shiro 自身提供的一套注解来完成。

    4.8.1、贴注解

        在 Controller 的方法上贴上 Shiro 提供的权限注解(@RequiresPermissions,@RequiresRoles)

      // 说明需要有这个权限才可以访问这个方法 
      @RequiresPermissions(value = "employee:list")
      @RequestMapping("list")
      public String listAll(@ModelAttribute("qo") EmployeeQueryObject employeeQueryObject, Model model,
          DepartmentQueryObject departmentQueryObject) {
        PageInfo<Employee> pageInfo = employeeService.selectForPage(employeeQueryObject);
        List<Department> departments = departmentService.selectAll();
        List<Role> roles = roleService.selectAll();
        model.addAttribute("departments", departments);
        model.addAttribute("roles", roles);
        model.addAttribute("pageInfo", pageInfo);
        return "employee/list";
      }
    

    4.8.2、配置注解扫描

        当扫描到 Controller 中有使用 @RequiresPermissions 注解时,会使用cglib动态代理为当前 Controller 生成代理对象,增强对应方法,进行权限校验

    <!-- <aop:config/> 会扫描配置文件中的所有advisor,并为其创建代理 -->
    <aop:config />
    <!-- Pointcut advisor通知器, 会匹配所有加了shiro权限注解的方法 -->
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>
    

    4.8.3、修改自定义Realm

    // 授权
      @Override
      protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 创建info对象
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 获取当前登录主体对象
        //Subject subject = SecurityUtils.getSubject();
        // 获取主体的身份对象(这里获取的对象与认证方法doGetAuthenticationInfo返回的SimpleAuthenticationInfo对象的第一个参数是同一个对象)
        Subject subject = SecurityUtils.getSubject();
        Employee employee = (Employee) subject.getPrincipal();
        // 判断是否是超级管理员
        if (employee.getAdmin()) {
          info.addRole("admin");
          // 给他所有的权限
          info.addStringPermission(" *:* ");
        } else {
          // 通过员工id拿到所有的角色
          List<Role> roles = employeeService.selectRolesById(employee.getId());
          for (Role role : roles) {
            info.addRole(role.getSn());
          }
          //通过员工id查询出所有的权限
          List<String> permissionByEmployeeId = employeeService
              .getPermissionByEmployeeId(employee.getId());
          info.addStringPermissions(permissionByEmployeeId);
        }
        return info;
      }
    

    4.8.4、配置自定义异常

     @ExceptionHandler(AuthorizationException.class)
      public String BussinessException(AuthorizationException exception, HttpServletResponse response,
          HandlerMethod method) throws IOException {
        exception.printStackTrace(); //方便开发的时候找bug
        //如果原本控制器的方法是返回jsonresult数据,现在出异常也应该返回jsonresult
        //获取当前出现异常的方法,判断是否有ResponseBody注解,有就代表需要返回jsonresult
        if (method.hasMethodAnnotation(ResponseBody.class)) {
          try {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().print(JSON.toJSONString(new ResponseResult(false, "没有权限操作")));
          } catch (IOException e1) {
            e1.printStackTrace();
          }
          return null;
        }
        //如果原本控制器的方法是返回视图页面,现在也应该返回视图页面
        return "common/nopermission";
      }
    

    4.8.5、Shiro标签集成FreeMarker

        在前端页面上,我们通常可以根据用户拥有的权限来显示具体的页面,如:用户拥有删除员工的权限,页面上就把删除按钮显示出来,否则就不显示删除按钮,通过这种方式来细化权限控制。我们需要使用Shiro标签来进行控制。要能够实现上面的控制,需要使用 Shiro 中提供的相关标签。

    4.8.5.1、拓展FreeMarker标签

        前端页面我们选择的是freemarker,而默认 freemarker 是不支持 shiro 标签的,所以需要对其功能做拓展,可以理解为注册 shiro 的标签,达到在freemarker 页面中使用的目的。

    public class ShiroFreeMarkerConfig extends FreeMarkerConfigurer {
      
      @Override
      public void afterPropertiesSet() throws IOException, TemplateException {
        //继承之前的属性配置,这不能省
        super.afterPropertiesSet();
        Configuration cfg = this.getConfiguration();
        cfg.setSharedVariable("shiro", new ShiroTags());//注册shiro 标签,这里可以换成自己想要其他的标签,但是一般建议是用shiro
      }
    }
    

    4.8.5.2、修改mvc.xml中的配置

        在mvc.xml 中把以前的FreeMarkerConfigurer修改成我们自定义的MyFreeMarkerConfig类

      <!-- 注册 FreeMarker 配置类 -->
      <bean class="cn.linstudy.shiro.ShiroFreeMarkerConfig">
        <!-- 配置 FreeMarker 的文件编码 -->
        <property name="defaultEncoding" value="UTF-8"/>
        <!-- 配置 FreeMarker 寻找模板的路径 -->
        <property name="templateLoaderPath" value="/WEB-INF/views/"/>
        <property name="freemarkerSettings">
          <props>
            <!-- 兼容模式 ,配了后不需要另外处理空值问题,时间格式除外 -->
            <prop key="classic_compatible">true</prop>
            <!-- 数字格式化 , 不会有,字符串的 -->
            <prop key="number_format">0.##</prop>
          </props>
        </property>
      </bean>
    

    4.8.5.3、常用标签

    4.8.5.3.1、authenticated标签

        authenticated标签里面囊括的表示的是已经通过了认证了的用户才会显示的前端界面

    <@shiro.authenticated> </@shiro.authenticated>
    
    4.8.5.3.2、notAuthenticated标签

        与authenticated标签相对立,表示为认证通过的用户。

    <@shiro.notAuthenticated></@shiro.notAuthenticated>
    
    4.8.5.3.3、principal 标签

        principal 标签表示的是输出当前用户的信息。,通常可以用来输出登录用户的用户名。

    <@shiro.principal property="name" />
    
    4.8.5.3.4、hasRole 标签

        hasRole表示验证当前用户是否拥有某些角色。

    <@shiro.hasRole name="admin">Hello admin!</@shiro.hasRole>
    
    4.8.5.3.5、hasAnyRoles 标签

        hasAnyRoles标签表示验证当前用户是否拥有这些角色中的任何一个,角色之间逗号分隔。

    <@shiro.hasAnyRoles name="admin,user">Hello admin</@shiro.hasAnyRoles>
    
    4.8.5.3.6、hasPermission 标签

    hasPermission 标签表示验证当前用户是否拥有该权限。

    <@shiro.hasPermission name="department:delete">删除</@shiro.hasPermission>
    

    4.8.6、重构权限加载方法

        这里要说的是一种思想,我们在项目中可能会遇到需要加载项目中加了@RequiresPermissions注解的权限,就会有一个类似加载权限的按钮。

    image-20210325114035329

        但是我们发现好像Shiro的@RequiresPermissions注解并没有提供name属性给我们,仅仅只有value属性,那么我们需要另辟蹊径来完成这个需求。
        Shiro的@RequiresPermissions注解有两个属性:

    1. value属性:这个属性是一个数组,也就是说一个请求映射方法可以运行配置多个权限。多个权限之间用逗号隔开
    value={"employee:list","employee:delete", }
    
    1. logical 属性:该属性根据配置属性值对当前用户是否有权限访问请求映射方法进行限制,他有两个值:

      Logical.AND: 必须同时拥有value配置所有权限才允许访问。
      Logical.OR:只需要拥有value配置所有权限中一个即可允许访问。

        我们可以约定@RequiresPermissions 注解中的value属性值(数组)中第一位为权限表达式, 第二位为权限名称。

      // shiro注解无法使用name属性,所以约定,value中第一个位置的值是权限表达式,第二个位置的值是权限名称.
      // 但是 logical 的值必须是Logical.OR
      @RequiresPermissions(value = {"employee:list", "员工列表"}, logical = Logical.OR)
      @RequestMapping("list")
      public String listAll(@ModelAttribute("qo") EmployeeQueryObject employeeQueryObject, Model model,
          DepartmentQueryObject departmentQueryObject) {
        PageInfo<Employee> pageInfo = employeeService.selectForPage(employeeQueryObject);
        List<Department> departments = departmentService.selectAll();
        List<Role> roles = roleService.selectAll();
        model.addAttribute("departments", departments);
        model.addAttribute("roles", roles);
        model.addAttribute("pageInfo", pageInfo);
        return "employee/list";
      }
    

        同时修改reload方法

     public void reload() {
        //一次性的把所有的权限信息查出来.
        List<Permission> permissionsOnDB = permissionMapper.selectAll();
        // 拿到所有方法
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = rmhm.getHandlerMethods();
        // 遍历所有方法
        for (HandlerMethod method : handlerMethods.values()) {
          // 判断方法上是否有RequiresPermissions注解
          RequiresPermissions annotation = method.getMethodAnnotation(RequiresPermissions.class);
          if (annotation != null) {
            // 如果不为空。说明是贴了注解的
            // 数组的第一个元素表示的是权限表达式
            String expression = annotation.value()[0];
            // 数组的第二个元素表示的是权限的名称
            String name = annotation.value()[1];
            // 在存入数据库之前先判断以下,如果数据库中没有才插入
            if (!permissionsOnDB.contains(expression)) {
              Permission permission = new Permission();
              permission.setName(name);
              permission.setExpression(expression);
              permissionMapper.insert(permission);
            }
          }
        }
      }
    

    五、Shiro密码加密

        加密的目的是从系统数据的安全考虑,如,用户的密码,如果我们不对其加密,那么所有用户的密码在数据库中都是明文,只要有权限查看数据库的都能够得知用户的密码,这是非常不安全的。所以,只要密码被写入磁盘,任何时候都不允许是明文, 以及对用户来说非常机密的数据,我们都应该想到使用加密技术,这里我们采用的是 MD5+盐+散列次数来进行加密。

    如何实现项目中密码加密的功能:

    1. 添加用户的时候,对用户的密码进行加密

    2. 登录时,按照相同的算法对表单提交的密码进行加密然后再和数据库中的加密过的数据进行匹配

    5.1、MD5+盐加密

        MD5 加密的数据如果一样,那么无论在什么时候加密的结果都是一样的,所以,相对来说还是不够安全,但是我们可以对数据加“盐”。同样的数据加不同的“盐”之后就是千变万化的,因为我们不同的人加的“盐”都不一样。这样得到的结果相同率也就变低了。

        盐一般要求是固定长度的字符串,且每个用户的盐不同。

        可以选择用户的唯一的数据来作为盐(账号名,身份证等等),注意使用这些数据作为盐要求是不能改变的,假如登录账号名改变了,则再次加密时结果就对应不上了。

    5.2、Md5Hash()

        Md5Hash()这个方法有三个参数,第一个参数表示需要加密的密码的明文,第二个参数表示加密时所需要的盐,第三个参数表示散列次数(加密几次),这样可以保证加密后的密文很难恢复和破解。

    5.3、注册用户(密码加密)

        在添加用户的时候,需要对用户的密码进行加密。

    @RequestMapping("checkUsername")
      @ResponseBody
      public ResponseResult checkUsername(String username, String password, HttpSession session) {
        Employee employee = employeeService.selectByUsername(username);
        if (employee == null) {
          // 进行MD5密码加密
          Md5Hash md5Hash = new Md5Hash(password, username, 1024);
          EmployeeInsertVO employeeVO = new EmployeeInsertVO(username, username, md5Hash.toString(),
              session.getAttribute("EMAIL_IN_SESSION").toString(), false, true);
          employeeService.regsiter(employeeVO);
          return new ResponseResult(true, "注册成功");
        } else {
          return new ResponseResult(false, "用户名已存在");
        }
      }
    
    

    5.4、登录

        在登录时, 先对前端传过来的密码进行与注册相同的相同算法的加密,再传给shiro进行认证处理即可。

    try {
            // 对传进来的密码进行加密
            Md5Hash md5Hash = new Md5Hash(password, username, 1024);
            UsernamePasswordToken token = new UsernamePasswordToken(username, md5Hash.toString());
            SecurityUtils.getSubject().login(token);
            Employee employee = employeeMapper.selectByUsername(username);
            return employee;
          } catch (UnknownAccountException e) {
            throw new CarBussinessException("用户名错误");
          } catch (IncorrectCredentialsException e) {
            throw new CarBussinessException("密码错误");
          }
    }
    

    六、Shiro集成EhCache

    6.1、Cache是什么

        Cache是缓存,他是计算机内存中一段数据 ,他的作用是 用来减轻DB的访问压力,从而提高系统的查询效率。

    image-20200530090656417

    6.2、使用缓存的原因

        我们在进行Debug的时候,我们会发现,一旦请求到需要权限控制的方法的时候,每请求一次他都会去调用自定义Realm中的 doGetAuthorizationInfo 方法获取用户的权限信息,这个时候对数据库造成的访问压力是十分大的,而且用户登陆后,授权信息一般很少变动,所以我们可以在第一次授权后就把这些授权信息存到缓存中,下一次就直接从缓存中获取,避免频繁访问数据库。

    6.3、集成EhCache

    6.3.1、引入依赖

    <!--引入shiro和ehcache-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>1.5.3</version>
    </dependency>
    

    6.3.2、添加缓存配置文件

    <ehcache>
        <defaultCache
                maxElementsInMemory="1000"
                eternal="false"
                timeToIdleSeconds="600"
                timeToLiveSeconds="600"
                memoryStoreEvictionPolicy="LRU">
        </defaultCache>
    </ehcache>
    

        配置文件属性详解:
    maxElementsInMemory: 缓存对象最大个数。

    **eternal **:对象是否永久有效,一但设置了,timeout 将不起作用。

    timeToIdleSeconds: 对象空闲时间,指对象在多长时间没有被访问就会失效(单位:秒)。仅当 eternal=false 对象不是永久有效时使用,可选属性,默认值是 0,也就是可闲置时间无穷大。

    timeToLiveSeconds:对象存活时间,指对象从创建到失效所需要的时间(单位:秒)。仅当 eternal=false 对象不是永久有效时使用,默认是 0,也就是对象存活时间无穷大。

    memoryStoreEvictionPolicy:当达到 maxElementsInMemory 限制时,Ehcache 将会根据指定的策略去清理内存。

    缓存策略一般有3种:

    1. 默认LRU(最近最少使用,距离现在最久没有使用的元素将被清出缓存)。

    2. FIFO(先进先出, 如果一个数据最先进入缓存中,则应该最早淘汰掉)。

    3. LFU(较少使用,意思是一直以来最少被使用的,缓存的元素有一个hit 属性(命中率),hit 值最小的将会被清出缓存)。

    6.3.2、配置缓存管理器

    <!--安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--注册自定义数据源-->
        <property name="realm" ref="employeeRealm"/>
        <!--注册缓存管理器-->
        <property name="cacheManager" ref="cacheManager"/>
    </bean>
    
    <!-- 缓存管理器 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 
        <!-- 设置配置文件 -->
        <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
    </bean>
    
    展开全文
  • SSL商业的价格昂贵,很多SEO优化人员觉得麻烦所以也就懒得去搞,其实前面不弄也没什么,只是网站流量起来后,就会出现各种想都想不到的事情,让你应接不暇。那么我们下面来介绍免费获得SSL证书步骤: 1、打开网站...

    原地址:https://www.ssfiction.com/seoyh/2008.html----猴子技术宅

    SSL商业的价格昂贵,很多SEO优化人员觉得麻烦所以也就懒得去搞,其实前面不弄也没什么,只是网站流量起来后,就会出现各种想都想不到的事情,让你应接不暇。那么我们下面来介绍免费获得SSL证书步骤:

    1、打开网站进入( tool.chinaz.com)站长之家,会点SEO的都知道这个网站。

    2、在首页网址填上使用的网站,并回车。

    填入网站,站长获得SSL_SEO优化

    3、如果SSL是拥有的可以忽略,如果没有就直接点击免费获取按钮。

    SSL协议免费获取方式!

    4、其他的都不要理会,直接填上相关信息后,点击核对并提交。

    SSL协议免费获取方式!

    域名身份如何自动验证

    1. 自动DNS验证原理

    提交证书申请后,CA机构会指定添加一条CNAME解析记录来验证域名的所有权,可以立即前往域名商后台添加指定的CNAME解析记录,等待CA机构的定时扫描审核,以最快最便捷的方式完成证书申请。

    3. 自动DNS验证原理

    切记,域名解析需提交站长工具提供的类型、域名记录、记录值,CA即可检测到并及时签发证书。

    私钥密码指引

    私钥密码是申请证书时的选填项,如图所示:

    获取SSL_SSL_SEO优化

    注意事项:

    1、如果填写了私钥密码,请您牢记该密码,该密码不支持找回和修改;

    2、该密码在证书下载完成进行解压时需要输入;

    3、在您的服务器上进行证书导入、导出、安装等操作时可能会需要输入;

    4、如果私钥密码不慎遗忘,请联系站长工具相关人员删除该证书,然后重新申请该域名证书。

    证书安装指引

    选择对应的格式证书下载得到的 www.domain.com.zip 文件,解压获得对应文件, 
    下面提供了4类服务器证书安装方法的示例:

    1. Apache 2.x 证书部署

    建议:openssl版本1.1.0f+

    Httpd版本(即apache)。建议:2.2.34+

    1.1 获取证书

    点击“下载”(选择PEM_Apache)会得到一个zip的压缩包,解压后有三个文件,分别是cer,crt和key后缀的(获得证书文件如www.domain.com.crt,www.domain.com.cer 和私钥文件 www.domain.com.key) 
    www.domain.com.crt 文件包括一段证书代码 “—–BEGIN CERTIFICATE—–”和“—–END CERTIFICATE—–”, 
    www.domain.com.cer 文件包括一段证书代码 “—–BEGIN CERTIFICATE—–”和“—–END CERTIFICATE—–”, 
    www.domain.com.key 文件包括一段私钥代码“—–BEGIN RSA PRIVATE KEY—–”和“—–END RSA PRIVATE KEY—–”。

    1.2 证书安装

    编辑Apache根目录下 conf/httpd.conf 文件, 
    找到 #LoadModule ssl_module modules/mod_ssl.so和 #Include conf/extra/httpd-ssl.conf,去掉前面的 #号注释; 
    编辑Apache根目录下 conf/extra/httpd-ssl.conf 文件,修改如下内容:

    
    <VirtualHost www.domain.com:443>
       DocumentRoot "/var/www/html"
       ServerName www.domain.com
       SSLEngine on
       SSLCertificateFile /usr/local/apache/conf/www.domain.com.cer
       SSLCertificateKeyFile /usr/local/apache/conf/www.domain.com.key
       SSLCertificateChainFile /usr/local/apache/conf/www.domain.com.crt
    </VirtualHost>
    

    配置完成后,重新启动 Apache 就可以使用 (www.domain.com)来访问了。

    注:

    配置文件参数说明
    SSLEngine on启用SSL功能
    SSLCertificateFile证书文件
    SSLCertificateKeyFile私钥文件
    SSLCertificateChainFile证书链文件

    1.3 http跳转https(建议非强制)

    编辑Apache根目录下 conf/httpd.conf 文件, 
    找到 LoadModule rewrite_module modules/mod_rewrite.so去掉前面的 # 
    到80(http)的virtualhost去配置 
    RewriteEngine on 
    RewriteCond %{SERVER_PORT} !^443$ 
    RewriteRule (.*) https://%{SERVER_NAME}/$1 [R]

    1.4 检测

    https的端口没做限制后(防火墙放行,端口转发正常),到s.tool.chinaz.com进行检测,评级达到6以上,在安全和兼容方面是较不错的。

    2. Nginx 证书部署

    2.1 获取证书

    证书下载选择pem(nginx SLB)格式,得到的压缩包解压后获得SSL证书文件 1_www.domain.com_bundle.crt 和私钥文件 2_www.domain.com.key,

    2.2 证书安装

    将域名 www.domain.com 的证书文件1_www.domain.com_bundle.crt 、私钥文件2_www.domain.com.key保存到同一个目录,例如/usr/local/nginx/conf目录下。 
    更新Nginx根目录下 conf/nginx.conf 文件如下:

    server{
      listen 443;
      server_namewww.domain.com;//填写绑定证书的域名
      ssl on;
      ssl_certificate 1_www.domain.com.crt;
      ssl_certificate_key 2_www.domain.com.key;
      ssl_session_timeout 5m;
      ssl_protocols TLSv1 TLSv1.1TLSv1.2;#按照这个协议配置
      ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
      //按照这个套件配置
      ssl_prefer_server_ciphers  on;
        location / {
              root html;//站点目录
              index index.html index.htm;
         }
     }

    配置完成后,先用 bin/nginx –t来测试下配置是否有误,正确无误的话,重启nginx。就可以使 https://www.domain.com来访问了。

    注:

    配置文件参数说明
    listen 443SSL访问端口号为443
    ssl on启用SSL功能
    ssl_certificate证书文件
    ssl_certificate_key私钥文件
    ssl_protocols使用的协议
    ssl_ciphers配置加密套件,写法遵循openssl标准

    2.3 使用全站加密,http自动跳转https(可选)

    对于用户不知道网站可以进行https访问的情况下,让服务器自动把http的请求重定向到https。 
    在服务器这边的话配置的话,可以在页面里加js脚本,也可以在后端程序里写重定向,当然也可以在web服务器来实现跳转。Nginx是支持rewrite的(只要在编译的时候没有去掉pcre) 
    在http的server里增加 rewrite ^(.*) https://$host$1 permanent; 
    这样就可以实现80进来的请求,重定向为https了。

    3. IIS 证书部署

    3.1 获取证书

    IIS文件夹内获得SSL证书文件 www.domain.com.pfx。

    3.2 证书安装

    IIS管理器_SSL_SEO优化

    1、打开IIS服务管理器,点击计算机名称,双击‘服务器证书’ 

    导入_SSL_SEO优化

    2、双击打开服务器证书后,点击右则的导入 

    导入证书_SSL_SEO优化

    3、选择证书文件,如果输入申请证书时有填写私钥密码需要输入密码,否则输入文件夹中密码文件keystorePass.txt的密码内容,点击确定。 参考私钥密码指引 

    IIS管理器_SSL_SEO优化

    4、点击网站下的站点名称,点击右则的绑定 

    站点名称_SSL_SEO优化

    5、打开网站绑定界面后,点击添加 

    添加网站绑定_SSL_SEO优化

    6、添加网站绑定内容:选择类型为https,端口443和指定对应的SSL证书,点击确定 

    网站绑定_SSL_SEO优化

    7、添加完成后,网站绑定界面将会看到刚刚添加的内容 

    4. Tomcat 证书部署

    4.1 获取证书

    如果申请证书时有填写私钥密码,下载可获得Tomcat文件夹,其中有密钥库 www.domain.com.jks; 
    如果没有填写私钥密码,证书下载包的Tomcat文件夹中包括密钥库文件www.domain.com.jks 与密钥库密码文件keystorePass.txt 
    当用户选择粘贴CSR时,不提供Tomcat证书文件的下载,需要用户手动转换格式生成,操作方法如下:

    可以通过 Nginx 文件夹内证书文件和私钥文件生成jks格式证书 
    转换工具: https://www.trustasia.com/tools/cert-converter.htm 
    使用工具时注意填写 密钥库密码,安装证书时配置文件中需要填写。

    4.2 证书安装

    配置SSL连接器,将 www.domain.com.jks文件存放到conf目录下,然后配置同目录下的 server.xml文件:

    <Connectorport="443"protocol="HTTP/1.1"SSLEnabled="true"maxThreads="150"scheme="https"secure="true"
    keystoreFile="conf/www.domain.com.jks"keystorePass="changeit"clientAuth="false"sslProtocol="TLS"/>

    注:

    配置文件参数说明
    clientAuth如果设为true,表示Tomcat要求所有的SSL客户出示安全证书,对SSL客户进行身份验证
    keystoreFile指定keystore文件的存放位置,可以指定绝对路径,也可以指定相对于 (Tomcat安装目录)环境变量的相对路径。如果此项没有设定,默认情况下,Tomcat将从当前操作系统用户的用户目录下读取名为 “.keystore”的文件。
    keystorePass密钥库密码,指定keystore的密码。(如果申请证书时有填写私钥密码,密钥库密码即私钥密码,否则填写密钥库密码文件中的密码)
    sslProtocol指定套接字(Socket)使用的加密/解密协议,默认值为TLS

    4.3 http自动跳转https的安全配置

    到conf目录下的web.xml。在 </welcome-file-list>后面, </web-app>,也就是倒数第二段里,加上这样一段

    <login-config>
    <!-- Authorization setting for SSL -->
    <auth-method>CLIENT-CERT</auth-method>
    <realm-name>Client Cert Users-only Area</realm-name>
    </login-config>
    <security-constraint>
    <!-- Authorization setting for SSL -->
    <web-resource-collection>
    <web-resource-name>SSL</web-resource-name>
    <url-pattern>/*</url-pattern>
     </web-resource-collection>
    <user-data-constraint>
    <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint></security-constraint>

    这步目的是让非ssl的connector跳转到ssl的connector去。所以还需要前往server.xml进行配置:

    <Connectorport="8080"protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="443"/>

    redirectPort改成ssl的connector的端口443,重启后便会生效。


    域名型证书吊销指引

    1. 提交工单

    寻求站长工具相关人员协助您完成证书吊销。

    2. 提供相关信息

    提供证书信息:包括证书ID、域名、证书序列号。

    3. 证书序列号获取方法

    3.1 下载证书到本地,双击打开

    序列号_SSL_SEO优化

    3.2 切换到【详细信息】,获取证书序列号

    序列号_SSL_SEO优化

    4. 重新验证域名身份

    站长工具相关人员会要求您完成相应的DNS验证或者文件验证,完成身份验证后,CA机构方可继续完成证书吊销流程。

    展开全文
  • 点击“开发者技术前线”,选择“星标” 让一部分开发者看到未来当前的疫情形势依然严峻,无论是上班上学去医院, 还是出门购物等,进出多数场合都需要出示健康码,可每次出示都要操作一番手机,偶尔...
  • 前言 很久以来,一直想写一篇微信支付有关的总结文档;一方面是总结自己的一些心得,另一方面也可以帮助别人,但是因种种原因未能完全理解透彻微信支付的几大支付方式,今天...商户微信浏览器以外的手机浏览器打开H.
  • JSP、Servlet学习笔记

    2021-10-27 12:38:05
    目录 JSP: 4种基本语法 3个编译指令 7个动作指令 9个内置对象 Servlet: ...JSP的本质是Servlet,每一个JSP页面就是一个Servlet...JSP生成的Servlet存放Tomcat的work路径对应的Web应用下。 JSP页面由系统编译成Servlet
  • 通过编写该需求文档说明,使用户可以详细地了解到软件的功能、环境、特点、受众等情况,从而使用软件时可以得到更好的用户体验。同时也以文字形式记录了用户需求,是与用户沟通后的结果。本文档将供开发人员和用户...
  • 第5章 会话及其会话技术

    千次阅读 2020-05-21 07:57:23
    例如,用户网站结算商品时,Web服务器必须根据请求用户的身份,找到该用户所购买的商品。Web开发中,服务器跟踪用户信息的技术成为会话技术。接下来,本章将针对会话及其会话技术进行详细的讲解。 5.1 会话概述...
  • 关于小程序的https访问问题

    千次阅读 2018-07-20 17:53:32
    把域名从http变成https,首先去腾讯云申请ssl证书,审核后,你就可以下载了,得到的 www.domain.com.zip 文件,解压获得3个文件夹,分别是Apache、IIS、Nginx 服务器的证书文件,(密码就里面的压缩包里面的txt) ...
  • 1.call、apply与bind都用于改变this绑定,但call、apply改变this指向的同时还会执行函数,而bind改变this后是返回一个全新的boundFcuntion绑定函数,这也是为什么上方例子中bind后还加了一对括号 ()的原因。...
  • 1.wdcp v3.2(linux+apache+nginx)系统模板为: 目前wdcp v3.2默认为nginx+apache引擎,用户登录wdcp界面可自助,很简单的设置。 A、首先申请》下载》解压SSL证书到本地电脑。然后登陆wdcp控制面板,点网站管理...
  • 1.05 3 系统的需求分析与设计 作为软件前期开发过程中,需要对项目进行需求分析,首先要了解业务具体做什么,实现什么样的功能,功能模块中细分每个功能模块的使用背景,只有这样软件开发中,才不会重复出现因为...
  • 本文提供完整代码示例,可见GitHub - YunaiV/SpringBoot-Labs: 一个涵盖六个专栏:Spring Boot 2.X、Spring Cloud、Spring Cloud Alibaba、Dubbo、分布式消息队列、分布式事务的仓库。希望胖友小手一抖,右上角来...
  • 本文说明了为什么这样做不是一个好主意,以及为什么不应该使用这种方法来保护生产中的敏感数据。 tl; dr:许多组织正在使用反向代理来保护其Elasticsearch基础架构。我们的观点是,代理服务器不能针对大量的广泛攻击...
  • Java对接微信、支付宝、银联第三方支付

    千次阅读 热门讨论 2021-04-07 09:05:38
    商户可以公众平台提交基本资料、业务资料、财务资料申请开通微信支付功能。 (2) 微信开放平台 微信开放平台是商户APP接入微信支付开放接口的申请入口,通过此平台可申请微信APP支付。 (3) 微信商户平台 微信...
  • tp剩余未验证内容

    2018-05-22 17:39:00
    new Image(宽度,高度) $(image).attr('src', ...).load(function(){....}) ...new Image的时候, 内存缓存中 的图像 如何装载进来, 要加入到文档的某个节点中来才能 生根,才能显示?? 使用 a...
  • [数据库] mysql

    2021-01-30 14:28:13
    本文将各类知识归类,并将全文知识点浓缩自问自查中,并且都写好目录,自问自查时可以随时跳转过去,方便大家系统的学习复习知识。 水平有限,有错误敬指正 食用方法 自问自查—阅读原文—自问自查–阅读原文… 无限...
  • 自主开发互联网医院系统模块介绍

    千次阅读 2020-08-05 16:49:53
    就诊人切换 可以添加的就诊人之间进行切换当前就诊患者 就诊二维码 就诊人列表中选择就诊人二维码,使用时,向医生出示此就诊码,其中包含姓名、登记号、手机号、就诊卡号、身份证号等信息。 药品清单 可以...
  • shiro

    2021-06-01 14:41:27
    1.1 什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。...
  • healthCode

    2020-09-06 10:37:34
    若为空,则forward到登录界面jsp;若不为空,则forward到日常健康申报表单jsp。 4. 日常健康申报 =============================================================================== 插:DailyReportDO UserDO ...
  • 全站SSL加密后安卓手机访问本博客时总时显示“该证书并非来自可信的授权中心”和Firefox浏览器总是显示“sec_error_unknown_issuer”警告,然而IE/Chrome/Safari上都显示非常正常,到底那里出了问题呢?...
  • 闪聚支付 第3章-微信支付接入指南

    千次阅读 2020-08-05 08:59:21
    10011 redirect_uri不能为空 10012 appid不能为空 10013 state不能为空 10015 公众号未授权第三方平台,检查授权状态 10016 支持微信开放平台的Appid,使用公众号Appid 第二步:获取openid 有了授权才能使用授权...
  • 党员管理系统毕业设计

    千次阅读 2021-03-16 21:04:58
    本文为软件工程课程设计的党员发展管理系统的实验,用于给大伙参一下,共五部分,设置了跳转,方便大家跳转到对应的地方,内容比较多,不足之处希望大伙提出来 目录 第一部分——项目小组规约 1.项目名称:党员发展...
  • Servlet

    2020-07-02 13:58:44
    } 六、重定向和请求转发【重点】 6.1 重定向 servlet中需要跳转到其他的页面或者servlet中,可以使用重定向方式。 该方式特点如下: 地址栏地址会变化。 至少有两次请求。 多次请求直接request对象不会共享,所以...
  • 软件工程导论(中)

    2020-05-05 11:37:46
    设计模型的内容应该包括: 宏观说明目标软件系统的整体结构,这种宏观的软件结构软件工程中称为“体系结构” 从高层抽象的角度刻画组成目标软件系统的模块以及他们之间的逻辑关联和协作关系 用户界面的设计——...
  • 新冠疫情来势汹汹,商场也成为高危场所,为避免人群聚集、病毒扩散,各地百货店、购物中心纷纷进入闭店或半闭店模式,据2月9日国务院联防联控机制新闻发布会的数据显示,前一时期大型百货商场、购物中心开业率仅35...
  • Spring Boot 安全框架 Spring Security 入门

    千次阅读 2020-04-30 02:19:15
    基本上,所有的开发的系统中,都必须做认证(authentication)和授权(authorization),以保证系统的安全性。???? 考虑到很多胖友对认证和授权有点分不清楚,艿艿这里引用一个网上有趣的例子: FROM《认证 ...

空空如也

空空如也

1 2 3 4 5
收藏数 98
精华内容 39
关键字:

请允许在后台出示界面才能跳转是什么意思