精华内容
下载资源
问答
  • 安全控件

    千次阅读 2018-04-17 19:05:07
    0x02 使用安全控件 随着移动互联网的普及和网上支付的盛行,越来越多的恶意软件病毒木马针对用户的键盘鼠标操作进行记录,从而窃取用户的账号密码等。因此,如何让用户的帐号信息输入更安全,如何防止木马和病毒...

    0x01 定义

        简单说,安全控件是一种为了提升账户安全性,防止账户密码等私密信息被木马或病毒窃取的客户端程序,安全控件大多小巧,可以通过 ActiveX 或者手工下载的方式安装。

    0x02 使用安全控件

        随着移动互联网的普及和网上支付的盛行,越来越多的恶意软件病毒木马针对用户的键盘鼠标操作进行记录,从而窃取用户的账号密码等。因此,如何让用户的帐号信息输入更安全,如何防止木马和病毒盗取用户帐号信息成为互联网信息安全的首要问题,在这样的背景下,要求我们必须通过安全控件这种技术手段加强对用户帐号信息的安全保护。
    安全控件一般通过以下方式保护用户输入信息的安全:
    1)  模拟密码框( ( 防止读取密码框) ) ;
    2)  模拟( ( 屏幕) ) 键盘( ( 防止监听键盘输入) ) ;
    3)  扫描后台程序,检测 k Hook  程序。
         关于钩子( Hook ): :钩子(Hook)是盗号密码的常用伎俩,日常生活中,鱼钩是用来钓鱼的,一旦鱼咬了钩,钩子就一直钩住鱼了,任凭鱼在水里怎么游,也逃不出鱼钩的控制。同样的,Windows 的钩子 Hook 也是用来钩东西的,比较抽象的是他是用来钩 Windows 事件或者消息的。最常见的就是鼠标和键盘钩子,用 Hook 钩子钩住鼠标、键盘,当你的鼠标、键盘有任何操作时,通过 Hook 就能知道他们都做了什么了。

    0x03 安全控件现状

       目前各家网银、第三方支付公司等所使用的安全控件存在明显的安全隐患,不能很好的保护用户的帐号信息安全,主要体现在:
    1)  安全控件面对内核级到应用级的多种钩子无能为力;
    2)  内存动 态捕获的木马更是防不胜防;
    3)  软键盘 保护 技术,没有有效的防止后台录 、截 屏软件工作。
           Windows 系统是建立在事件驱动的机制上的,也就是整个Windows 系统都是通过消息的传递来实现的。而钩子是 Windows 系统中非常重要的系统接口,用它可以截获并处理传递给其他应用程序的消息,来完成普通应用程序难以实现的功能。钩子可以监视系统或进程中的各种事件消息,截获发往目标窗口的消息并进行处理。
           因此,可以在系统中安装自定义的钩子,监视系统中特定事件的发生,完成特定的功能,比如截获键盘、鼠标的输入,屏幕取词,日志监视等等。可见,利用钩子可以实现许多特殊而有用的功能,很多黑客也正是利用了钩子技术制作出各式各样的木马,用于窃取用户的帐号信息。为了避免各种钩子程序获取用户帐号信息,安全控件就必须优先于各种钩子程序处理用户的输入信息以及用户端屏幕显示(屏显)。

           可以确认目前的各种钩子程序主要通过两种方式获取用户的帐户信息:第一类是键盘类 Hook 程序,包括从应用层到核心层多种 Hook 方式;第二类是屏显Hook 程序。常见的类型有:COM 接口调用【IE COM 接口 】、API Hook【Windows Procedure--(WM_CHAR, WM_KEYDOWN )、API 函数(PostMessage)hook 】、消息钩子【SendMessage(WM_GETTEXT)  、SetWindowsHookEX(WH_KEYBOARD)、SetWindowsHookEx(WH_KEYBOARD_LL)】、API 调用【GetKeyboardState 、GetAsyncKeyState 、GetKeyState】、标准设备过滤驱动【键盘过滤驱动(FDO)】、键盘类驱动【针对 PS/2 键盘类驱动(kbdClass)Dispatch
    Routine Hook;、针对 USB 键盘类驱动(hidusb、usbhub 】、Inline Hook、OBJECT Hook 、IDT Hook、端口读写 、屏幕显示类

    0x04 工作原理

    键盘消息处理原理

           对于采用 PS2 接口的键盘,键盘事件是通过键盘中断来实现的。当有一个键被按下或者抬起,就会引发一个中断事件,此时, CPU 会查找并调用相关中断服务处理函数进行处理,中断函数处理后便会调用上层驱动设置的回调函数(如果存在 )进行键盘事件已完成的向上传递过程。而对于采用 USB 接口的键盘,则较为复杂。读取 kbdclass 设备的 IRP 请求,会被转化成针对 USB 设备的 URB 请求块,传输方式设置为中断或批量传输方式,并通过 USB 总线驱动投递到 USB 物理总线上。当按键时, USB 控制器(像键盘这种 legacy 设备,会使用 UHCI 类型控制器进行管理)会过设置与操作系统共享的内存块中的数据结构,也即 TD 块( Transfer Descriptor ),并中断操作系统进行处理。而操作系统中断调用对应中断服务处理函数进行该数据的处理,并完成 URB 请求,继而完成对应的 IRP请求并向上层驱动传递。

    键盘消息传递安全漏洞分析

    ring0 按键拦截过滤技术
    A. Attach 到键盘类驱动( kbdClass )的设备栈上( PS/2 和 USB ),进行
    键盘数据过滤;
    B. 针对 PS/2 键盘实现 i8042ptr 的过滤驱动;
    C. 针对 USB 键盘实现的过滤驱动( hidusb 等);
    D. 针对 PS/2 键盘类驱动( kbdClass ) Dispatch Routine Hook ;
    E. 针对 USB 键盘类驱动( hidusb 、 usbhub 等) Dispatch Routine Hook ;
    F. KbdClassServiceCallBack Routine Hook ( PS/2 和 USB );
    G. 针对 PS/2 键盘的 PSIDT Hook ;
    H. 针对 PS/2 键盘的 IOAPIC 编程;
    I. 针对 PS/2 键盘的 I/O

    ring3 按键拦截过滤技术
    A. WM_GETTEXT 消息
    B. Hook PostMessage API
    C. SetWindowsHookEx 设置消息钩子
     WH_KEYBORAD
     WH_KEYBORAD_LL
     其他消息钩子
    D. RawInput
    E. GetKeyboardState  和 GetAsyncKeyState API

    欢迎大家分享更好的思路,热切期待^^_^^ !

    展开全文
  • web上面开发安全控件使用Activex控件

    千次阅读 2016-02-25 11:03:27
    首先要说明的是 web上activex是门被抛弃的技术。...通过网页下载控件的中间过程受到操作系统、杀毒软件、浏览器等多方面因素限制,这使你的控件产品未必在所有用户的电脑上正常使用;3。最恶心

      首先要说明的是 web上用activex是门被抛弃的技术。为什么这么说,主要基于以下几个原因:1.功能过于强大,强大到一旦安装,他所具有的权限可以做任何事,极易被人利用做坏事(相信大家都有浏览网页后莫名被安装流氓软件的经历,当然流氓软件利用的方式不止控件一种);2。通过网页下载控件的中间过程受到操作系统、杀毒软件、浏览器等多方面因素限制,这使你的控件产品未必能在所有用户的电脑上正常使用;3。最恶心的一点,未签名没有正规安全证书的控件,在最新的浏览器上(IE7等)默认安全策略是直接屏蔽掉的,而你如果想要获取这个信任,需要向微软等少数单位申请,花费有多少?据说是每年4千多。。。
       看了以上如果你还是想要知道步骤,如下:
    一。用Vc++6.0新建工程里的向导创建MFC activeX controlWizard,细节不说了,根据你控件需要了,略有不同,问题不会太大。
     
    二。你控件代码的主xx.cpp(非xxCtr.cpp)文件中添加安全接口函数,否则每次运行控件时IE都会给出安全提示,很烦!
    #include "comcat.h"
    #include "Objsafe.h"
     
    // 本控件的CLSID,注册表用
    const GUID CDECL CLSID_SafeItem ={ 0x7AE7497B, 0xCAD8, 0x4E66,
                          { 0xA5,0x8B,0xDD,0xE9,0xBC,0xAF,0x6B,0x61 } };
    // 版本控制
    const WORD _wVerMajor = 1;
    // 次版本号
    const WORD _wVerMinor = 0;
     
     
    /
    // CICCardApp::InitInstance - DLL initialization
     
    BOOL CICCardApp::InitInstance()
    {
        BOOL bInit = COleControlModule::InitInstance();
     
        if (bInit)
        {
        
        }
     
        return bInit;
    }
     
     
    //
    // CICCardApp::ExitInstance - DLL termination
     
    int CICCardApp::ExitInstance()
    {
        return COleControlModule::ExitInstance();
    }
     
     
    //
     
    // 创建组件种类
    HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription)
    {
        ICatRegister* pcr = NULL ;
        HRESULT hr = S_OK ;
     
        hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
                NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
        if (FAILED(hr))
            return hr;
     
        // Make sure the HKCR\Component Categories\{..catid...}
        // key is registered.
        CATEGORYINFO catinfo;
        catinfo.catid = catid;
        catinfo.lcid = 0x0409 ; // english
     
        // Make sure the provided description is not too long.
        // Only copy the first 127 characters if it is.
        int len = wcslen(catDescription);
        if (len>127)
            len = 127;
        wcsncpy(catinfo.szDescription, catDescription, len);
        // Make sure the description is null terminated.
        catinfo.szDescription[len] = '\0';
     
        hr = pcr->RegisterCategories(1, &catinfo);
            pcr->Release();
     
        return hr;
    }
     
    // 注册组件种类
    HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
    {
        // Register your component categories information.
        ICatRegister* pcr = NULL ;
        HRESULT hr = S_OK ;
        hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
                    NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
        if (SUCCEEDED(hr))
        {
           // Register this category as being "implemented" by the class.
           CATID rgcatid[1] ;
           rgcatid[0] = catid;
           hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
        }
        if (pcr != NULL)
            pcr->Release();
        return hr;
    }
    // 卸载组件种类
    HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
    {
        ICatRegister* pcr = NULL ;
        HRESULT hr = S_OK ;
     
        hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
                NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
        if (SUCCEEDED(hr))
        {
           // Unregister this category as being "implemented" by the class.
           CATID rgcatid[1] ;
           rgcatid[0] = catid;
           hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid);
        }
     
        if (pcr != NULL)
            pcr->Release();
     
        return hr;
    }
     
     
    // DllRegisterServer - Adds entries to the system registry
    STDAPI DllRegisterServer(void)
    {
        HRESULT hr;
     
        AFX_MANAGE_STATE(_afxModuleAddrThis);
     
        if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
            return ResultFromScode(SELFREG_E_TYPELIB);
     
        if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
            return ResultFromScode(SELFREG_E_CLASS);
     
        // 标记控件初始化安全.
        // 创建初始化安全组件种类
        hr = CreateComponentCategory(CATID_SafeForInitializing,
                   L"Controls safely initializable from persistent data!");
        if (FAILED(hr))
            return hr;
        // 注册初始化安全
        hr = RegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);
        if (FAILED(hr))
            return hr;
     
        // 标记控件脚本安全
        // 创建脚本安全组件种类 
        hr = CreateComponentCategory(CATID_SafeForScripting, L"Controls safely scriptable!");
        if (FAILED(hr))
            return hr;
        // 注册脚本安全组件种类
        hr = RegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);
        if (FAILED(hr))
            return hr;
     
        return NOERROR;
    }
     
     
    //
    // DllUnregisterServer - Removes entries from the system registry
     
    STDAPI DllUnregisterServer(void)
    {
        HRESULT hr;
     
        AFX_MANAGE_STATE(_afxModuleAddrThis);
     
        if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))
            return ResultFromScode(SELFREG_E_TYPELIB);
     
        if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))
            return ResultFromScode(SELFREG_E_CLASS);
     
        // 删除控件初始化安全入口.
        hr=UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);
        if (FAILED(hr))
            return hr;
        // 删除控件脚本安全入口
        hr=UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);
        if (FAILED(hr))
            return hr;
     
    //
        return NOERROR;
    }
     
    三。添加自己的代码到控件文件xxCtr.cpp中,这里最好通过向导添加View->ClassWard->automation。
     
    四。编译生成你的xx.ocx控件文件,这个文件其实就是个DLL函数库,只是后缀不同而已。
     
    五。制作xx.inf文件用于之后的空间打包压缩。红色部分为需要根据你自己情况修改的地方。
    [version]   
    signature="$CHICAGO$"   
    AdvancedINF=1.0   
    [Add.Code]   
    xx.ocx=xx.ocx   
    [xx.ocx]   
    file-win32-x86=thiscab   
    clsid={351FC603-D784-4B8D-BA2C-33C212EC5BAA}   
    FileVersion=1,0,0,1   
    RegisterServer=yes   
     
    六。打包压缩成xx.cab文件。你需要用到给控件签名的一些工具:cabarc.exe、signcode.exe,另外你可能需要用于制作自己试验证书的工具:makecert.exe、cert2spc.exe。
    制作根证书MyCert.cer和私钥myKey.PVK:
    makecert -r  -sv "myKey.PVK"  -n CN="xx Electric Industry Co. Ltd."   MyCert.cer
    CERT2SPC MyOKIcert.cer MyOKIcert.spc
    打包压缩空间为xx.cab:
    cabarc -s 6144 n xx.cab xx.ocx xx.inf
    数字签名cab文件
    双击signocode.exe文件,图形化签名,注意要通过自定义选择自己的证书和私钥而不是选择系统已有的。
     
    七。在你要用空间的Web上添加代码:
                 <td colspan="2"><object id='t1' codeBase=/UserMode/xx.cab#version=1,0,0,1 classid='clsid:F3530445-A287-4A0B-AC3E-C4AF2B63EBA7' height=0 width=0 ></object></td>
     
    八。调用控件内方法。通过javascript直接调用控件对象的方法就行了,比如调用空间中存在的一个download()方法: document.getElementByID("t1").download();
     
    八。客户端使用,其他人从网上下载到你的控件后,如果是Window XP系统的话,需要先导入证书为可信任(详细到Google上搜索导入证书,很容易),再安装控件,之后才能用;而如果是Window2000用户则只要安装控件就可以使用了。
     
    九。控件的删除。Internet选项-〉设定-〉显示对象,直接看到你的xx.cab文件,Del掉就行了,注册表中信息会自动消除掉,当然你也可以直接到注册表中删除对应xx.inf中列出的键值的项。
     
    十。升级。只要在源代码中更改xx.rc文件中对应的那些1.0.0.1为你的新版本号,并同时更改你调用控件的web上的版本号就可以了,下一次客户访问这个页面时会自动提示安装新控件,新控件会覆盖就版本的控件。


    有什么更好的方法或者资源愿意分享的可以留下联系方式。

    展开全文
  • 解决:ActiveX控件不能使用

    千次阅读 2019-01-06 15:13:26
    有些古董网站是包含了IE浏览器的ActiveX控件,如果不加载ActiveX控件就会导致部分页面不能显示,甚至检索功能都不了。 点名某某大学的校内学位论文网站(我是一个没有感情的杀手 解决ActiveX控件不能使用,分...

    有些古董网站是包含了IE浏览器的ActiveX控件,如果不加载ActiveX控件就会导致部分页面不能显示,甚至检索功能都用不了。

    点名某某大学的校内学位论文网站(我是一个没有感情的杀手

    解决ActiveX控件不能使用,分三个步骤:

    1.设置IE的安全配置。

    单击菜单栏的“工具”->“Internet选项”->“安全”选项卡->“自定义级别”。

    把“下载已签名的Activex控件”和“Activex控件和插件”选为启用。再确定。

    2.重启计算机。配置才会生效。

    3.先确定一下配置是不是改好了。然后登录需要ActiveX控件的网站。

    如若还不能正常显示,则打开菜单栏的“工具”,在“Activex筛选”前打勾。

    刷新页面。

    如若还不能正常显示,则打开菜单栏的“工具”,选择“兼容视图设置”,将该网站“添加”至兼容网站。刷新即可。

    展开全文
  • 3、如以上两种情况均以排除,仍不能正常使用,很可能由于未能正常下载安全控件所致,请将IE浏览器设置恢复为默认值后再进行安全控件下载及登录。 a) 点击浏览器菜单栏上的工具→Internet选项→安全,选择Internet...
  • SpringSecurity安全控件使用指南

    千次阅读 多人点赞 2019-12-08 21:50:01
    SpringSecurity是一个能够基于Spring的应用程序提供声明式安全保护的安全性框架,它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了SpringIOC(控制反转)和AOP(面向切面编程)功能,为应用系统提供安全...

    写在前面: 从2018年底开始学习SpringBoot,也用SpringBoot写过一些项目。现在想对学习Springboot的一些知识总结记录一下。如果你也在学习SpringBoot,可以关注我,一起学习,一起进步。

    相关文章:

    【Springboot系列】Springboot入门到项目实战


    目录

    SpringSecurity简介

    1、简介

    2、Security适配器

    SpringSecurity核心类

    1、Authentication类

    2、SecurityContextHolder类

    3、UserDetails类

    4、UserDetailsService类

    5、GrantedAuthority类

    6、DaoAuthenticationProvider类

    7、PasswordEncoder类

    SpringSecurity的验证机制

    1、SpringSecurity的验证机制

    搭建项目

    1、新建Springboot项目

    2、项目结构。

    编写代码

    1、登录页面

    2、登录成功页面

    3、Security配置类

    4、认证成功处理类LoginSuccessHandle

    5、测试控制器

    6、测试应用

    用户和权限保存在数据库中

    1、修改后项目的目录结构。

    2、添加依赖

    3、配置基本属性。

    4、创建用户类和角色权限实体类

    5、创建数据访问接口

    6、创建自定义服务类Service

    7、修改配置类

    8、测试应用

    9、案例下载


    SpringSecurity简介

    1、简介

    SpringSecurity是一个能够基于Spring的应用程序提供声明式安全保护的安全性框架,它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了SpringIOC(控制反转)和AOP(面向切面编程)功能,为应用系统提供安全访问控制功能,减少了为系统安全控制编写大量重复代码的工作。

    官网地址:https://spring.io/projects/spring-security

    安全框架主要包含两个操作。

    认证:确认用户可以访问当前系统。

    授权:确定用户在当前系统中是否能够执行某个操作,即用户所拥有的功能权限。

    2、Security适配器

    创建一个自定义类继承WebSecurityConfigurerAdapter,并在改类中使用@EnableWebSecurity注解就可以通过重写config方法类配置所需要的安全配置。

    WebSecurityConfigurerAdapter是SpringSecurity为Web应用提供的一个适配器,实现了WebSecurityConfigurerAdapter接口,提供了两个方法用于重写开发者需要的安全配置。

    protected void configure(HttpSecurity http) throws Exception {
    }
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    }

    configure(HttpSecurity http)方法中可以通过HTTPSecurity的authorizeRequests()方法定义那些URL需要被保护、那些不需要被保护;通过formLogin()方法定义当前需要用户登录的时候,跳转到的登录页面。

    configure(AuthenticationManagerBuilder auth)方法用于创建用户和用户的角色。

    用户认证

    SpringSecurity是通过configure(AuthenticationManagerBuilder auth)完成用户认证的。使用AuthenticationManagerBuilder的inMemoryAuthentication()方法可以添加用户,并给用户指定权限。

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("aaa").password("{noop}1234").roles("USER");
        auth.inMemoryAuthentication().withUser("admin").password("{noop}admin").roles("ADMIN", "DBA");
    }

    上面的代码中添加了两个用户,其中一个用户名是“aaa”,密码是“1234”,用户权限是“USER”;另一个用户名是“admin”,密码是“admin”,用户权限有两个,分别是“ADMIN”和“DBA”。需要注意的是SpringSecurity保存用户权限的时候,会默认使用“ROLE_”,也就是说“USER”实际上是“ROLE_USER”,“ADMIN”实际是“ROLE_ADMIN”,“DBA”实际上是“ROLE_DBA”。

    当然,也可以查询数据库获取用户和权限。下面有写到。

    用户授权

    SpringSecurity是通过configure(HttpSecurity http)完成用户授权的。

    HTTPSecurity的authorizeRequests()方法有多个子节点,每个macher按照它们的声明顺序执行,指定用户可以访问的多个URL模式。

    • antMatchers使用Ant风格匹配路径。
    • regexMatchers使用正则表达式匹配路径。 

    在匹配了请求路径后,可以针对当前用户的信息对请求路径进行安全处理。下表是SpringSecurity提供的安全处理方法。

    方法 用途
    anyRequest 匹配所有请求路径
    access(String) Spring EL 表达式结果为true时可以访问
    anonymous() 匿名可以访问
    denyAll() 用户不能访问
    fullyAuthenticated() 用户完全认证可以访问(非remember-me下自动登录)
    hasAnyAuthority(String...) 如果有参数,参数表示权限,则其中任何一个权限可以访问
    hasAnyRole(String...) 如果有参数,参数表示角色,则其中任何一个角色可以访问
    hasAuthority(String...) 如果有参数,参数表示权限,则其权限可以访问
    hasIpAddress(String) 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
    hasRole(String) 如果有参数,参数表示角色,则其角色可以访问
    permitAll() 用户可以任意访问
    rememberMe() 允许通过remember-me登录的用户访问
    authenticated() 用户登录后可访问

    示例代码如下:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();    //安全器令牌
        http.authorizeRequests()
                .antMatchers("/login").permitAll()
                .antMatchers("/", "/home").hasRole("USER")
                .antMatchers("/admin/**").hasAnyRole("ADMIN", "DBA")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .usernameParameter("loginName").passwordParameter("password")
                .defaultSuccessUrl("/main")
                .failureUrl("/login?error")
                .and()
                .logout()
                .permitAll()
                .and()
                .exceptionHandling().accessDeniedPage("/accessDenied");
    }
    1. http.authorizeRequests() 开始请求权限配置。
    2. antMatchers("/login").permitAll() 请求匹配“/login”,所有用户都可以访问。
    3. antMatchers("/", "/home").hasRole("USER")  请求匹配“/”和“/home”,拥有“ROLE_USER”角色的用户可以访问。
    4. antMatchers("/admin/**").hasAnyRole("ADMIN", "DBA")  请求匹配“/admin/**”,拥有“ROLE_ADMIN”或“ROLE_DBA”角色的用户可以访问。
    5. anyRequest().authenticated()  其余所有的请求都需要认证(用户登录)之后才可以访问。
    6. formLogin()  开始设置登录操作
    7. loginPage("/login")  设置登录页面的访问地址
    8. usernameParameter("loginName").passwordParameter("password")  登录时接收传递的参数“loginName”的值作为用户名,接收传递参数的“password”的值作为密码。如果不设置,默认“username”为用户名,“password”为密码
    9. defaultSuccessUrl("/main")  指定登录成功后转向的页面。
    10. failureUrl("/login?error")  指定登录失败后转向的页面和传递的参数。
    11. logout()   设置注销操作
    12. permitAll()  所有用户都可以访问。
    13. exceptionHandling().accessDeniedPage("/accessDenied")  指定异常处理页面

    SpringSecurity核心类

    SpringSecurity核心类包括Authentication、SecurityContextHolder、UserDetails、UserDetailsService、GrantedAuthority、DaoAuthenticationProvider和PasswordEncoder。只要掌握了这些SpringSecurity核心类,SpringSecurity就会变得非常简单。

    1、Authentication类

    Authentication用来表示用户认证信息,用户登录认证之前,SpringSecurity会将相关信息封装为一个Authentication具体实现类的对象,在登录认证成功之后又会生成一个信息更全面、包含用户权限等信息的Authentication对象,然后把它保存在SecurityContextHolder所持有的SecurityContext中,供后续的程序进行调用,如访问权限的鉴定等。

    SecurityContextHolder中的getContext()方法

    public static SecurityContext getContext() {
    	return strategy.getContext();
    }

    2、SecurityContextHolder类

    SecurityContextHolder是用来保存SecurityContext的。SecurityContext中含有当前所访问系统的用户的详细信息。默认情况下,SecurityContextHolder将使用ThreadLocal来保存SecurityContext,这也就意味着在处于同一线程的方法中,可以从ThreadLocal获取到当前的SecurityContext。

    SpringSecurity使用一个Authentication对象来描述当前用户的相关信息。SecurityContextHolder中持有的是当前用户的SecurityContext,而SecurityContext持有的是代表当前用户相关信息的Authentication的引用。这个Authentication对象不需要我们自己创建,在与系统交互的过程中,SpringSecurity会自动创建相应的Authentication对象,然后赋值给当前的SecurityContext。开发过程中常常需要在程序中获取当前用户的相关信息,比如最常见的获取当前登录用户的用户名。

     String username = SecurityContextHolder.getContext().getAuthentication().getName();

     获取UserDetails类,该类中包含用户认证相关等信息。

    UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

    3、UserDetails类

    UserDetails是SpringSecurity的一个核心接口。其中定义了一些可以获取用户名,密码、权限等与认证相关的信息的方法。SpringSecurity内部使用UserDetails实现类大都是内置的User类,要使用UserDetails,也可以直接使用该类。在SpringSecurity内部,很多需要使用用户信息的时候,基本上都是使用UserDetails,比如在登录认证的时候。

    通常需要在应用中获取当前用户的其他信息,如E-mail、电话等。这时存放在Authentication中的principal只包含认证相关信息的UserDetails对象可能就不能满足我们的要求了。这时可以实现自己的UserDetails,在该实现类中可以定义一些获取用户其他信息的方法,这样将来就可以直接从当前SecurityContext的Authentication的principal中获取这些信息。

    UserDetails是通过UserDetailsService的loadUserByUsername()方法进行加载的,UserDetailsService也是一个接口,我们也需要实现自己的UserDetailsService来加载自定义的UserDetails信息。

    新建用户表的Service类实现UserDetailsService接口,来重写UserDetailsService的loadUserByUsername()方法,根据用户名查询当前用户信息并返回,返回的类必须继承Security内置的User类。

    @Service
    public class SysUserService implements UserDetailsService{
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
           
        }
    }

    4、UserDetailsService类

    Authentication.getPrincipal() 的返回类型是Object。

    Object getPrincipal();

    但很多情况下返回的其实是一个UserDetails的实例。登录认证的时候SpringSecurity会通过UserDetailsService的loadUserByUsername()方法获取对应的UserDetails进行认证,认证通过后会将该UserDetails赋给认证通过的Authentication的Principal,然后在把该Authentication存入SecurityContext。之后如果需要使用用户信息,可以通过SecurityContextHolder获取存放在SecurityContext中的Authentication的principal,转为UserDetails类。

    UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

    5、GrantedAuthority类

    Authentication的getAuthorities()方法可以返回当前Authentication对象用户的所有的权限。

    Collection<? extends GrantedAuthority> getAuthorities();

    即当前用户拥有的权限。其返回值是一个GrantedAuthority类型的数组,每一个GrantedAuthority对象代表赋予给当前用户的一种权限。GrantedAuthority是一个接口,其通常是通过UserDetailsService进行加载,然后赋予UserDetails的。

    GrantedAuthority中只定义了一个getAuthority()方法,该方法返回一个字符串,表示对应的权限,如果对应权限不能用字符串表示,则返回null

    获取当前用户的所有权限,存放到List集合中。

    UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    List<String> roleCodes=new ArrayList<>();
    for (GrantedAuthority authority : userDetails.getAuthorities()) {
    	roleCodes.add(authority.getAuthority());
    }

    6、DaoAuthenticationProvider类

    SpringSecurity默认了会使用DaoAuthenticationProvider实现AuthenticationProvider接口,专门进行用户认证的处理。DaoAuthenticationProvider在进行认证的时候需要一个UserDetailsService来获取用户的信息UserDetails,其中包括用户名,密码和所拥有的权限等。如果需要改变认证的方式,开发者可以实现自己的AuthenticationProvider。

    7、PasswordEncoder类

    在SpringSecurity中,对密码的加密都是由PasswordEncoder来完成的。在SpringSecurity中,已经对PasswordEncoder有了很多实现,包括md5加密,SHA-256加密等,开发者只需要直接拿来用就可以。在DaoAuthenticationProvider中,有一个就是PasswordEncoder熟悉,密码加密功能主义靠它来完成。

    SpringSecurity的验证机制

    1、SpringSecurity的验证机制

    SpringSecurity大体上是由一堆Filter实现的,Filter会在SpringMVC前拦截请求。Filter包括登出Filter(LogoutFilter)、用户名密码验证Filter(UsernamePasswordAuthenticationFilter)之类。Filter在交由其他组件完成细分的功能,最常用的UsernamePasswordAuthenticationFilter会持有一个AuthenticationManager引用,AuthenticationManager是一个验证管理器,专门负责验证。但AuthenticationManager本身并不做具体的验证工作,AuthenticationManager持有一个AuthenticationProvider集合,AuthenticationProvider才是做验证工作的组件,验证成功或失败之后调用对应的Handler。

    搭建项目

    1、新建Springboot项目

    创建一个SpringBoot项目,添加SpringSecurity和Thymeleaf依赖。

    pom.xml完整代码。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.2.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.mcy</groupId>
        <artifactId>security-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>security-demo</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.thymeleaf.extras</groupId>
                <artifactId>thymeleaf-extras-springsecurity5</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>

    2、项目结构。

    编写代码

    1、登录页面

    用于测试的HTML页面,用了bootstrap的样式文件。

    login.html登录页面,主要有一个form表单和账号密码输入框,用于向login请求提交username和password,从而进行登录。Security默认登录地址login,退出地址为logout。代码如下。

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Security登录</title>
        <link rel="stylesheet" href="static/bootstrap/dist/css/bootstrap.css">
    </head>
    <body>
        <div class="panel panel-primary" style="width: 500px; margin: 5% auto;">
            <div class="panel-heading">登录</div>
            <div class="panel-body">
                <form action="login" method="post" class="form-horizontal">
                    <!--用户名或密码错误提示-->
                    <div th:if="${param.error != null}">
                        <div class="alert alert-danger">
                            <p color="red">用户名或密码错误!</p>
                        </div>
                    </div>
                    <!--注销提示-->
                    <div th:if="${param.logout != null}">
                        <div class="alert alert-success">
                            <p color="red">注销成功!</p>
                        </div>
                    </div>
    
                    //sec:authorize可以判断用户是否登录,用户权限,设置该菜单是否显示
                    <p sec:authorize="isAnonymous()">未登录显示</p>
                    <p sec:authorize="isAuthenticated()">登录显示</p>
    
                    <div class="input-group input-sm">
                        <label class="input-group-addon"><i class="glyphicon glyphicon-user"></i></label>
                        <input type="text" class="form-control" name="username" placeholder="请输入用户名">
                    </div>
                    <div class="input-group input-sm">
                        <label class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></label>
                        <input type="password" class="form-control" name="password" placeholder="请输入密码">
                    </div>
                    <div class="form-actions">
                        <input type="submit" class="btn btn-block btn-primary btn-default" value="登录">
                    </div>
                </form>
            </div>
        </div>
    </body>
    </html>

    用于用户名或密码错误提示。

    <div th:if="${param.error != null}">
        <div class="alert alert-danger">
            <p color="red">用户名或密码错误!</p>
        </div>
    </div>

    退出,注销提示。

    <div th:if="${param.logout != null}">
        <div class="alert alert-success">
            <p color="red">注销成功!</p>
        </div>
    </div>

    注意这些提示都是SpringSecurity里边自带的。

    sec:authorize用于判断用户是否登录,用户是否拥有哪些角色权限,一般在前台页面控制菜单是否显示。

    2、登录成功页面

    home.html是ROLE_USER用户登录之后显示的页面,同时提供了一个超链接到admin页面,代码如下。

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Home页面</title>
        <link rel="stylesheet" href="static/bootstrap/dist/css/bootstrap.css">
    </head>
    <body>
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">Home页面</h3>
            </div>
        </div>
        <h3>
            <p>欢迎[<span color="red" th:text="${user}">用户名</span>]访问Home页面!
            您的权限是:<span color="red" th:text="${role}">权限</span></p>
            <p><a href="admin">访问admin页面</a></p>
            <p><a href="logout">安全退出</a></p>
        </h3>
    </body>
    </html>

    admin.html是ROLE_ADMIN用户登录之后显示的页面,同时提供了一个到dba页面的超链接,代码如下。

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Admin页面</title>
        <link rel="stylesheet" href="static/bootstrap/dist/css/bootstrap.css">
    </head>
    <body>
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">Admin页面</h3>
            </div>
        </div>
        <h3>
            <p>欢迎[<span color="red" th:text="${user}">用户名</span>]访问Admin页面!
                您的权限是:<span color="red" th:text="${role}">权限</span></p>
            <p><a href="dba">访问dba页面</a></p>
            <p><a href="logout">安全退出</a></p>
        </h3>
    </body>
    </html>

    dba.html页面只是显示简单的欢迎语句,代码如下。

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>dba页面</title>
        <link rel="stylesheet" href="static/bootstrap/dist/css/bootstrap.css">
    </head>
    <body>
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">dba页面</h3>
            </div>
        </div>
        <h3>
            <p>欢迎[<span color="red" th:text="${user}">用户名</span>]访问dba页面!
                您的权限是:<span color="red" th:text="${role}">权限</span></p>
            <p><a href="logout">安全退出</a></p>
    
            
            <p sec:authorize="isAnonymous()">未登录显示</p>
            <p sec:authorize="isAuthenticated()">登录显示</p>
            <p sec:authorize="hasRole('ROLE_ADMIN')">权限包含ROLE_ADMIN显示</p>
            <p sec:authorize="!hasRole('ROLE_ADMIN')">权限不包含ROLE_ADMIN登录显示</p>
        </h3>
    </body>
    </html>
    

    accessDenied.html是访问拒绝页面,如果登录的用户没有权限访问该页面,会进行提示,代码如下。

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>访问拒绝页面</title>
        <link rel="stylesheet" href="static/bootstrap/dist/css/bootstrap.css">
    </head>
    <body>
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">AccessDenied页面</h3>
            </div>
        </div>
        <h3>
            <p>欢迎[<span color="red" th:text="${user}">用户名</span>],您没有权限访问页面!
                您的权限是:<span color="red" th:text="${role}">权限</span></p>
            <p><a href="logout">安全退出</a></p>
        </h3>
    </body>
    </html>

    3、Security配置类

    在项目中新建一个security包,在该包下新建一个WebSecurityConfig类,继承WebSecurityConfigurerAdapter类,用于处理SpringSecurity的用户认证和授权操作,设置页面访问权限。

    configure(AuthenticationManagerBuilder auth)和configure(HttpSecurity http)两个方法中分别打印了一句话,用于启动项目时的跟踪调试。

    successHandler(new LoginSuccessHandle())用于处理登出成功之后的操作,LoginSuccessHandle类用于处理不同用户跳转到不同页面。

    具体代码如下。

    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    
    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        /**
         * 用户认证操作
         * @param auth
         * @throws Exception
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            System.out.println("WebSecurityConfig   configure(AuthenticationManagerBuilder auth) 方法被调用。。。。。。");
            //添加用户,并给予权限
            auth.inMemoryAuthentication().withUser("aaa").password("{noop}1234").roles("USER");
            auth.inMemoryAuthentication().withUser("admin").password("{noop}admin").roles("ADMIN", "DBA");
        }
    
        /**
         * 用户授权操作
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            System.out.println("WebSecurityConfig   configure(HttpSecurity http) 方法被调用。。。。。。");
            http.csrf().disable();    //安全器令牌
            http.authorizeRequests()    //开始请求权限配置。
                    .antMatchers("/login", "/static/**").permitAll()
                    .antMatchers("/", "/home").hasRole("USER")
                    .antMatchers("/admin/**").hasAnyRole("ADMIN", "DBA")
                    .anyRequest().authenticated()   //其余所有的请求都需要认证(用户登录)之后才可以访问。
                    .and()
                    .formLogin()    //开始设置登录操作
                    .loginPage("/login")   //设置登录页面的访问地址
                    //.defaultSuccessUrl("/main")  //指定登录成功后转向的页面。
                    .successHandler(new LoginSuccessHandle())    //登录成功跳转,LoginSuccessHandle处理不同权限跳转不同页面
                    .failureUrl("/login?error")    //指定登录失败后转向的页面和传递的参数。
                    .and()
                    .logout().permitAll()       //退出
                    .and()
                    .exceptionHandling().accessDeniedPage("/accessDenied"); //指定异常处理页面
        }
    }

    4、认证成功处理类LoginSuccessHandle

    认证成功处理类LoginSuccessHandle,实现了AuthenticationSuccessHandler接口,是Spring用来处理用户认证授权并跳转到指定URL的。

    重新onAuthenticationSuccess方法,获取当前用户的权限,根据权限跳转到指定的URL路径,代码如下。

    import javax.servlet.Servlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import java.io.IOException;
    import java.util.Set;
    
    public class LoginSuccessHandle  implements AuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)throws IOException {
            //authentication.getAuthorities() 获取当前用户的权限
            Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
    
            //获取到登陆者的权限,然后做跳转
            if (roles.contains("ROLE_ADMIN")){
                response.sendRedirect("/admin");
                return;
            }else if (roles.contains("ROLE_USER")){
                response.sendRedirect("/home");
                return;
            }else {
                response.sendRedirect("/accessDenied");
            }
        }
    }

    5、测试控制器

    新建一个IndexController控制器,提供响应login,home,admin,dba,AccessDenied请求的方法。每个方法通过getUsername()方法获得当前认证用户的用户名,通过getAuthorith()方法获取当前认证用户的权限,并设置到ModelMap当中。

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import java.util.ArrayList;
    import java.util.List;
    
    @Controller
    public class IndexController {
        @RequestMapping(value = {"/", "/login"}, method = RequestMethod.GET)
        public String index(){
            return "login";
        }
    
        @RequestMapping("/home")
        public String homePage(ModelMap map){
            map.put("user", getUsername());
            map.put("role", getAuthority());
            return "home";
        }
    
        @RequestMapping("/admin")
        public String admin(ModelMap map){
            map.put("user", getUsername());
            map.put("role", getAuthority());
            return "admin";
        }
    
        @RequestMapping("/dba")
        public String dba(ModelMap map){
            map.put("user", getUsername());
            map.put("role", getAuthority());
            return "dba";
        }
    
        @RequestMapping("/accessDenied")
        public String accessDenied(ModelMap map){
            map.put("user", getUsername());
            map.put("role", getAuthority());
            return "accessDenied";
        }
    
        /**
         * 获取当前用户名称
         * @return
         */
        private String getUsername(){
            //获取当前用户名称
            String username = SecurityContextHolder.getContext().getAuthentication().getName();
            System.out.println("username="+username);
            return username;
        }
    
        private String getAuthority(){
            //获得Authentication对象,表示用户认证信息
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            List<String> roles = new ArrayList<>();
            //将角色名称添加到List集合
            for(GrantedAuthority a: authentication.getAuthorities()){
                roles.add(a.getAuthority());
            }
            System.out.println("role="+roles);
            return roles.toString();
        }
    }

    6、测试应用

    运行项目可以看到WebSecurityConfig类中重写的两个方法已经执行,说明自定义的用户认证和用户授权工作已经生效。

    在浏览器中输入URL测试应用:

    http://localhost:8080 “/”、“/login”、“/home”、“admin”等任何一个当前项目的请求都会被重定向到http://localhost:8080/login页面,因为没有登录,,用户没有访问权限。login页面中,用sec:authorize写了两个标签一个是登录成功显示,一个是未登录显示,没有登录,就只显示了一个。sec:authorize一般用于控制菜单是否显示,效果如图。

     

    输入错误的账号密码,会有提示用户名或密码错误。如图。

    输入用户名:aaa,密码:1234,登录,该用户是“ROLE_USER”,跳转到home页面,如图。

    单击超链接“访问admin页面”,由于当前用户的权限只是“ROLE_USER”,不能访问admin页面,所以会跳转到访问拒绝页面,如图。

    单击超链接“安全退出”,会退出到登录页面,登录页面提示“注销成功!”,如图。

    输入用户名:admin,密码admin,登录,该用户是“ROLE_USER”和“ROLE_DBA”权限,跳转到admin页面,如图。

    单击超链接“访问dba页面”,由于当前用户的权限是“ROLE_ADMIN”和“ROLE_DBA”,可以访问dba页面,所以会跳转到dba页面,如图。在dba页面代码中,写了四行用sec:authorize判断是否显示的标签,两行判断是否登录,两行判断该用户是否有这个角色,条件成立才显示标签内容,效果如图。

     

    通过测试可以看到,项目已经使用SpringSecurity实现了用户认证和用户授权。

    上面的案例并没有把用户和权限存放到数据中,在实际开发中,用户和权限肯定是存放在数据库中,下面就来看看如何把数据存放到数据库中。

    用户和权限保存在数据库中

    1、修改后项目的目录结构。

    2、添加依赖

    在原有的项目中,修改pom.xml文件,添加MySQL和JPA依赖。

    这里使用的是JPA操作数据库,对JPA不了解的可以访问Spring-Data-Jpa入门

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    3、配置基本属性。

    在src/main/resources下找到application.properties文件,在该配置文件中配置数据源和jpa相关的属性,需要在数据库中先新建一个security数据库。

    #数据源信息配置
    #数据库地址,jpa数据库名,需要在数据库中先建一个jpa数据库
    spring.datasource.url=jdbc:mysql://localhost:3306/security?serverTimezone=GMT%2B8
    #用户名
    spring.datasource.username=root
    #密码
    spring.datasource.password=root
    #链接数据库驱动
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    #指定数据库方言
    spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
    #配置在日志中显示SQL语句
    spring.jpa.show-sql=true
    #指定自动创建|更新|验证数据库表结构等配置,配置成updata
    #表示如果数据库中存在持久化类对应的表就不创建,不存在就创建对应的表
    spring.jpa.hibernate.ddl-auto=update
    spring.jpa.hibernate.use-new-id-generator-mappings=false

    4、创建用户类和角色权限实体类

    SysUser权限类,代码如下。

    import javax.persistence.*;
    
    @Entity
    @Table(name = "sys_role")
    public class SysRole {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;     //主键
        private String name;   //权限名称
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

    SysUser用户类

    import javax.persistence.*;
    import java.util.List;
    
    @Entity
    @Table(name = "sys_user")
    public class SysUser {
        private Integer id;
        private String name;
        private String username;
        private String password;
        private List<SysRole> roles;
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        @ManyToMany(cascade=CascadeType.REFRESH,fetch=FetchType.EAGER)
        @JoinTable(name="sys_user_role",joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="role_id"))
        public List<SysRole> getRoles() {
            return roles;
        }
    
        public void setRoles(List<SysRole> roles) {
            this.roles = roles;
        }
    }

    SysUser类用来保存用户数据,其中username表示用户名,password表示密码,name表示用户姓名,roles表示用户权限的List集合,用户和权限的关系是多对多关系。

    5、创建数据访问接口

    在项目中新建一个repository包,在该包下新建一个UserRepository接口和RoleRepository接口,继承JpaRepository接口,UserRepository接口中写一个根据用户名去查询的方法findByUsername,遵循Spring-Data-Jpa命名规范,代码如下。

    import com.mcy.securitydemo.entity.SysUser;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface UserRepository extends JpaRepository<SysUser, Integer> {
        //根据登录用户名查询用户
        SysUser findByUsername(String username);
    }
    import com.mcy.securitydemo.entity.SysRole;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface RoleRepository extends JpaRepository<SysRole, Integer> {
    
    }

    6、创建自定义服务类Service

    在项目中新建一个service包,在该包下新建一个UserService类和RoleService类,UserService类实现了UserDetailsService接口,登录认证的时候SpringSecurity会通过UserDetailsService的loadUserByUsername()方法获取对应的UserDetails进行认证。

    UserService类重写了UserDetailsService接口中的loadUserByUsername()方法,在方法中调用持久层接口的findByUsername方法通过JPA进行数据库验证,传递的参数是页面接收到的username。最后将获得的用户名,密码和权限保存到org.springframework.security.core.userdetails.User类中并返回,该User类是SpringSecurity内部的实现,专门用于保存用户名,密码,权限等与认证相关的信息。

    UserService类代码

    import com.mcy.securitydemo.entity.SysRole;
    import com.mcy.securitydemo.entity.SysUser;
    import com.mcy.securitydemo.repository.UserRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 需要实现UserDetailsService接口
     * 因为在SpringSecurity中配置的相关参数需要是UserDetailsService类的数据
     */
    @Service
    public class UserService implements UserDetailsService {
    
        //注入持久层接口UserRepository
        @Autowired
        private UserRepository userRepository;
    
        /**
         * 重写UserDetailsService接口中的loadUserByUsername方法,通过该方法查询对应的用户
         * 返回对象UserDetails是SpringSecurity的一个核心接口。
         * 其中定义了一些可以获取用户名,密码,权限等与认证相关信息的方法。
         * @param username
         * @return
         * @throws UsernameNotFoundException
         */
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            //调用持久层接口findByUsername方法查询用户。
            SysUser user = userRepository.findByUsername(username);
            if(user == null){
                throw new UsernameNotFoundException("用户名不存在");
            }
            //创建List集合,用来保存用户权限,GrantedAuthority对象代表赋予当前用户的权限
            List<GrantedAuthority> authorities = new ArrayList<>();
            //获得当前用户权限集合
            List<SysRole> roles = user.getRoles();
            for(SysRole role: roles){
                //将关联对象role的name属性保存为用户的认证权限
                authorities.add(new SimpleGrantedAuthority(role.getName()));
            }
            //此处返回的是org.springframework.security.core.userdetails.User类,该类是SpringSecurity内部的实现
            //org.springframework.security.core.userdetails.User类实现了UserDetails接口
            return new User(user.getUsername(), user.getPassword(), authorities);
        }
    
        //保存方法
        public void save(SysUser user) {
            userRepository.save(user);
        }
    }

    RoleService类代码

    import com.mcy.securitydemo.entity.SysRole;
    import com.mcy.securitydemo.repository.RoleRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class RoleService {
        @Autowired
        private RoleRepository roleRepository;
    
        public void save(SysRole role) {
            roleRepository.save(role);
        }
    }

    7、修改配置类

    修改WebSecurityConfig类设置认证方式,修改后的WebSecurityConfig代码如下。

    import com.mcy.securitydemo.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    
    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private UserService userService;
    
        /**
         * 用户认证操作
         * @param auth
         * @throws Exception
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            System.out.println("WebSecurityConfig   configure(AuthenticationManagerBuilder auth) 方法被调用。。。。。。");
            //添加用户,并给予权限
            auth.inMemoryAuthentication().withUser("aaa").password("{noop}1234").roles("USER");
            auth.inMemoryAuthentication().withUser("admin").password("{noop}admin").roles("ADMIN", "DBA");
            //设置认证方式
            auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
        }
    
        /**
         * 用户授权操作
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            System.out.println("WebSecurityConfig   configure(HttpSecurity http) 方法被调用。。。。。。");
            http.csrf().disable();    //安全器令牌
            http.authorizeRequests()    //开始请求权限配置。
                    .antMatchers("/login", "/static/**").permitAll()
                    .antMatchers("/", "/home").hasRole("USER")
                    .antMatchers("/admin/**").hasAnyRole("ADMIN", "DBA")
                    .anyRequest().authenticated()   //其余所有的请求都需要认证(用户登录)之后才可以访问。
                    .and()
                    .formLogin()    //开始设置登录操作
                    .loginPage("/login")   //设置登录页面的访问地址
                    //.defaultSuccessUrl("/main")  //指定登录成功后转向的页面。
                    .successHandler(new LoginSuccessHandle())    //登录成功跳转,LoginSuccessHandle处理不同权限跳转不同页面
                    .failureUrl("/login?error")    //指定登录失败后转向的页面和传递的参数。
                    .and()
                    .logout().permitAll()       //退出
                    .and()
                    .exceptionHandling().accessDeniedPage("/accessDenied"); //指定异常处理页面
        }
    }
    

    8、测试应用

    打开MySQL数据库,新建security数据库。运行项目根据对象之间关系,JPA会在数据库中自动创建sys_user, sys_role和中间表sys_user_role三张表。

    test类位置

    因为数据库中没有数据,可以通过test类添加测试数据,添加了三个权限和两个用户。在test类中运行如下代码。

    @Autowired
    private RoleService roleService;
    @Autowired
    private UserService userService;
    
    @Test
    void contextLoads() {
    SysRole role = new SysRole();
    role.setName("ROLE_USER");
    roleService.save(role);
    SysRole role1 = new SysRole();
    role1.setName("ROLE_ADMIN");
    roleService.save(role1);
    SysRole role2 = new SysRole();
    role2.setName("ROLE_DBA");
    roleService.save(role2);
    
    SysUser user = new SysUser();
    user.setName("张三");
    //用于对密码加密
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    user.setPassword(encoder.encode("123456"));
    user.setUsername("123456");
    List<SysRole> roles = new ArrayList<>();
    roles.add(role);    //给123456用户添加ROLE_USER权限
    user.setRoles(roles);
    userService.save(user);
    
    SysUser user1 = new SysUser();
    user1.setName("李四");
    //用于对密码加密
    user1.setPassword(encoder.encode("123456"));
    user1.setUsername("system");
    List<SysRole> roles1 = new ArrayList<>();
    roles1.add(role1);    //给system用户添加ROLE_USER权限
    roles1.add(role2);
    user1.setRoles(roles1);
    userService.save(user1);

    运行后数据库中数据如图(密码是加密过后的密码)

    在浏览器中输入http://localhost:8080/login访问登录页面,登录用户名为123456, 密码为123456的用户。登录成功页面如图。

    和上面aaa用户权限一样,所以能访问的页面也和aaa用户一样。system用户所添加的权限和上面的admin用户权限一样,大家还可以自行添加权限和用户进行测试。

    9、案例下载

    案例Demo下载地址:https://github.com/machaoyin/security-demo

    最后有什么不足之处,欢迎大家指出,期待与你的交流。

    展开全文
  • 由于工作的需要,隔三差五的就要登陆微信商户平台,查看公司微信交易的数据,可是前段时间,突然提示我「你没有...微信商户平台多数查看操作是无需安全控件的,但涉及到转账和提现等高风险操作时,会校验本地的安全...
  • 自从支付宝(中国)网络技术有限公司宣布支付宝对 Firefox 的支持之后,很多 Firefox 忠实用户已经下载并试用了该安全控件。这里分享几则使用过程中的常见问题。 Q: 到哪里下载 支付宝安全控件 for Firefox ? A: 您...
  • 赶快来本站免费下载使用吧! 软件特色 1、内存小,易便携,超过10mb; 2、大幅度的增加输入账号和密码的安全性; 3、江西银行登入企业和个人网上银行的必备软件。 软件安装教程 1、在本站下载解压
  • 前几天听 bugs 说,国内某C2C网站打算这个月开始禁止Firefox用户登录其网站,而说到原因,又是“出于用户安全考虑”,“Firefox无法支持其‘安全控件’”云云。 我没用过这家公司的服务,因此我也想就这家公司...
  • 解决办法: 1、卸载已将安装的控件。 2、删除安装文件(我的是在C盘)。 3、删除注册表安装文件。(cmd中输入regedit 进入注册表) 4、重新安装控件。 5、设置受信任的站点。
  • 前几天听 bugs 说,国内某C2C网站打算这个月开始禁止Firefox用户登录其网站,而说到原因,又是“出于用户安全考虑”,“Firefox无法支持其‘安全控件’”云云。我没用过这家公司的服务,因此我也想就这家公司发表...
  • 若您在win7和Vista系统下安装了安全控件与数字证书,依然无法输入密码或提示证书未安装,请您参考以下步骤设置您的浏览器: 第一步:任意打开一个网页,选择点击浏览器上方的 “工具Internet选项”; 第二步:...
  • 浏览器对安全控件的支持

    千次阅读 2016-10-15 10:42:44
    网银对输入账户和密码的内容,一般采取安全控件来避免被hook 很早以前,NPAPI是支持IE和其它内核浏览器的,但是在IE发展到一定阶段后,宣布放弃了npapi转到了ActiveX。所以对于IE系列浏览器而言,ActiveX是开发...
  • 1、问题表现: 当访问引用控件的网页时,NTKO OFFICE文档控件窗口不会出现,导致相关的引用控件的Javacript语句无法...为了解决NTKO OFFICE文档控件不能自动装载的问题,首先来看看浏览器装载对象涉及到的步骤。
  • WEB密码安全输入控件

    千次阅读 2013-11-18 20:08:43
    登录的用户名和密码都是在网络中通过明文传送,安全性较低,可以使用插件形式对密码进行加密后再传送,使用键盘钩子拦截键盘输入内容,防止被其它工具记录,类似银行网银安全输入控件。  为了保证每次加密的结果的...
  • 这是本控件开发完成后的一个...ActiveX控件大多是使用C++来开发的,而我对他们并熟悉,因此考虑使用熟悉的C#编写ActiveX控件。 ActiveX 控件注册 首先,建立一个Windows控件库 项目 My_HtmlControl,并拖入一个text
  • ActiveX控件不安全方法漏洞的检测技术
  • Outlook Security Manager (全称 - Security Manager for Microsoft Outlook) 是一个单线程的编程工具,可以让你实现旁路安全设置,以及避免安全警告或快速附件和应用程序,其结合了 Microsoft Outlook. Outlook ...
  • <br />前几天听 bugs 说,国内某C2C网站打算这个月开始禁止Firefox用户登录其网站,而说到原因,又是“出于用户安全考虑”,“Firefox无法支持其‘安全控件’”云云。   我没用过这家公司的服务,...
  • 控件为何不能自动装载?--全面总结

    千次阅读 2012-11-01 13:51:53
    1、问题表现: 当访问引用控件的网页时,控件窗口不会出现,导致相关的引用控件的...为了解决控件不能自动装载的问题,首先来看看浏览器装载对象涉及到的步骤。(暂时不考虑如防病毒软件或者其他过滤工具的影响)
  • 还得把IE的安全性改的乱七八糟,还支持猎豹,在猎豹下居然导致死机。 昨晚在Win7 64位上安装都正常。填验证码时要启动UsbKey。总是签名失败,虽然状态都是正常的。卸载再装。再插Usbkey也没用...
  • 保证标准控件安全

    2007-08-08 11:47:00
    ASP.NET保证标准控件安全 ASP.NET 标准控件是一组用于创建窗体的控件,在页面发回到服务器之前,用户可在这些窗体中键入或选择信息。标准控件是从 Control 类继承的 Web 服务器控件。Web 服务器的示例包括 ...
  • ActiveX控件已经被广泛的应用在安全输入方面,通过ActiveX控件,开发者可以轻易的通过加密算法取得加密后的字符串,以保证数据传输过程的安全。但是ActiveX控件的拘束性也显而易见 —— 仅IE浏览器提供支持。有一些...
  • 配置ActiveX控件在网页中下载安装

    千次阅读 2016-08-11 10:47:49
    记得曾经转载了一篇关于C#开发ActiveX控件的文章:使用C#开发ActiveX控件(zt) ,最近在项目中的使用过程中,发现不能自动下载Activex控件,这需要手动的为使用Activex的客户端注册,显然这是不适应的。 按道理说...
  • Xceed Real-Time Zip for ...它可以和Silverlight完美结合,因为它需要使用中间存储。 具体功能: .NET技术 100%使用C#托管代码编写。(其中Xceed Real-Time Zip for Silverlight Blueprint版本提供了完
  • web客户端ocx控件远程下载

    千次阅读 2013-09-25 09:37:49
    我想如果在网页上放了ACTIVEX控件,如果浏览器用户允许,它可以自动注册,不过你要在网页的OBJECT说明中指明如机器上没有此控件,它下载的位置,这可在CODEBASE属性中说明。 很简单: 在网页上你的OBJECT的CODE
  • 1、介绍 如果你曾经在网页或者ASP中使用过com对象,你可能会发现,有时候会出现这样讨厌的对话框 这是因为你的控件没有被标记为安全的,对于初始化不安全或者对于脚本不安全,甚至兼而有之。你每打开一次这

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 45,981
精华内容 18,392
关键字:

安全控件下载了还是不能用