├── .idea ├── compiler.xml ├── dictionaries │ └── sunlin.xml ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── README.md ├── app ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── security │ └── app │ ├── authentication │ ├── OwnAuthenctiationFailureHandler.java │ ├── OwnAuthenticationSuccessHandler.java │ └── openid │ │ ├── OpenIdAuthenticationProvider.java │ │ ├── OpenIdAuthenticationSecurityConfig.java │ │ ├── OpenIdAuthenticationToken.java │ │ ├── OpenidAuthenticationFilter.java │ │ └── impl │ │ └── AppSocialAuthenticationFilterPostProcessor.java │ ├── authorization │ └── config │ │ ├── AuthorizationServerConfig.java │ │ ├── ResourceServerConfig.java │ │ └── TokenStoreConfig.java │ ├── controller │ └── AppSecurityController.java │ ├── exception │ └── AppException.java │ ├── jwt │ └── LocalJwtTokenEnhancer.java │ ├── social │ ├── AppSignUpUtils.java │ └── SpringSocialConfigurePostProcessor.java │ └── validate │ └── RedisValidateCodeRepository.java ├── browser ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── security │ │ └── browser │ │ ├── authentication │ │ ├── OwnAuthenctiationFailureHandler.java │ │ └── OwnAuthenticationSuccessHandler.java │ │ ├── config │ │ ├── BrowserSecurityBeanConfig.java │ │ └── BrowserSecurityConfig.java │ │ ├── controller │ │ └── BrowserSecurityController.java │ │ ├── logout │ │ └── LoginOutSuccessHandler.java │ │ └── session │ │ ├── AbstractSessionStrategy.java │ │ ├── ExpiredSessionStrategy.java │ │ ├── InvalidSessionStrategy.java │ │ └── SessionValidateCodeRepository.java │ └── resources │ └── resources │ ├── banding.html │ ├── logout.html │ ├── session-invalid.html │ ├── signIn-bak.html │ ├── signIn.html │ ├── signUp.html │ └── static │ ├── css │ ├── htmleaf-demo.css │ ├── login │ │ └── login.css │ └── normalize.css │ ├── img │ └── icon │ │ ├── mobile.png │ │ ├── qq.png │ │ └── wechat.png │ └── js │ ├── html5shiv.min.js │ ├── jquery-2.1.1.min.js │ └── login │ └── login.js ├── core ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── security │ └── core │ ├── SecurityCoreConfig.java │ ├── authencation │ ├── AbstractChannelSecurityConfig.java │ └── sms │ │ ├── SmsCodeAuthenticationConfig.java │ │ ├── SmsCodeAuthenticationFilter.java │ │ ├── SmsCodeAuthenticationProvider.java │ │ └── SmsCodeAuthenticationToken.java │ ├── authorize │ ├── AuthorizeConfigManager.java │ ├── AuthorizeConfigProvider.java │ ├── BaseAuthorizeConfigManager.java │ └── BaseAuthorizeConfigProvider.java │ ├── properties │ ├── BaseSocialProperties.java │ ├── BrowserProperties.java │ ├── ImageCodeProperties.java │ ├── LoginResponseType.java │ ├── OAuth2ClientProperties.java │ ├── OAuth2Properties.java │ ├── QQProperties.java │ ├── SecurityConstants.java │ ├── SecurityProperties.java │ ├── SessionProperties.java │ ├── SmsProperties.java │ ├── SocialProperties.java │ ├── ValidateCodeProperties.java │ └── WeixinProperties.java │ ├── service │ └── SuperUserDetailsService.java │ ├── social │ ├── AbstractSocialController.java │ ├── config │ │ ├── LocalSpringSocialConfigure.java │ │ └── SocialConfig.java │ ├── interfaces │ │ └── SocialAuthenticationFilterPostProcessor.java │ ├── qq │ │ ├── api │ │ │ ├── QQ.java │ │ │ └── impl │ │ │ │ └── QQApiImpl.java │ │ ├── config │ │ │ └── QQAutoConfig.java │ │ ├── connect │ │ │ ├── QQAdapter.java │ │ │ ├── QQAuth2Template.java │ │ │ ├── QQConnectionFactory.java │ │ │ └── QQServiceProvider.java │ │ └── entity │ │ │ └── QQUserInfo.java │ ├── view │ │ ├── ConnectResultView.java │ │ └── ConnectStatusView.java │ └── wechat │ │ ├── api │ │ ├── WeiXin.java │ │ ├── WeiXinImpl.java │ │ └── WeiXinUserInfo.java │ │ ├── config │ │ └── WeiXinAutoConfiguration.java │ │ └── connect │ │ ├── WeiXinAccessGrant.java │ │ ├── WeiXinConnectionFactory.java │ │ ├── WeiXinServiceProvider.java │ │ ├── WeiXinTemplate.java │ │ └── WeixinAdapter.java │ ├── store │ └── CoreRedisTokenStore.java │ ├── support │ ├── SimpleResponse.java │ └── SocialUserInfo.java │ ├── util │ └── Tools.java │ └── validator │ ├── config │ ├── ValidateCodeBeanConfig.java │ └── ValidateCodeConfig.java │ ├── controller │ └── ValidatorCodeController.java │ ├── entity │ ├── ImageCode.java │ └── ValidateCode.java │ ├── enums │ └── ValidateCodeType.java │ ├── exception │ └── ValidateCodeException.java │ ├── filter │ └── ValidatorCodeFilter.java │ ├── image │ ├── ImageCodeProcessor.java │ └── ImageValidateCodeGenerator.java │ ├── inteface │ ├── ValidateCodeGenerator.java │ └── ValidateCodeRepository.java │ ├── processor │ ├── AbstractValidateCodeProcessor.java │ ├── ValidateCodeProcessor.java │ └── ValidateCodeProcessorHolder.java │ └── sms │ ├── DefaultSmsCodeSender.java │ ├── SmsCodeProcessor.java │ ├── SmsCodeSender.java │ └── SmsValidateCodeGenerator.java ├── demo ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── security │ │ │ ├── DemoApplication.java │ │ │ ├── exception │ │ │ └── UserException.java │ │ │ ├── handle │ │ │ └── ExceptionHandle.java │ │ │ ├── provider │ │ │ └── DemoAuthorizeConfigProvider.java │ │ │ ├── service │ │ │ ├── DemoUserService.java │ │ │ └── impl │ │ │ │ └── DemoUserServiceImpl.java │ │ │ ├── social │ │ │ ├── ConnectionSignUp.java │ │ │ └── MyUserDetailsServiceImpl.java │ │ │ ├── validator │ │ │ ├── MyConstraint.java │ │ │ └── MyConstraintValidator.java │ │ │ ├── web │ │ │ ├── aspect │ │ │ │ └── TimeAspect.java │ │ │ ├── async │ │ │ │ ├── AsyncController.java │ │ │ │ ├── DeferredResultHolder.java │ │ │ │ ├── MockQueue.java │ │ │ │ └── QueueListener.java │ │ │ ├── config │ │ │ │ ├── SwaggerConfig.java │ │ │ │ └── WebConfig.java │ │ │ ├── controller │ │ │ │ ├── DemoFileUpload.java │ │ │ │ └── DemoUserController.java │ │ │ ├── dao │ │ │ │ ├── DemoUserDetailsDao.java │ │ │ │ └── impl │ │ │ │ │ └── DemoUserDetailsDaoImpl.java │ │ │ ├── dto │ │ │ │ ├── DemoUser.java │ │ │ │ └── UserQueryCondition.java │ │ │ ├── entity │ │ │ │ ├── BaseEntity.java │ │ │ │ ├── FileInfo.java │ │ │ │ └── User.java │ │ │ ├── enums │ │ │ │ ├── EnabledStatus.java │ │ │ │ ├── LockStatus.java │ │ │ │ └── ResultEnums.java │ │ │ ├── filter │ │ │ │ └── TimeFilter.java │ │ │ ├── generator │ │ │ │ └── DemoImageCodeGenerator.java │ │ │ ├── interceptor │ │ │ │ └── TimeInterceptor.java │ │ │ ├── mapper │ │ │ │ └── UserMapper.java │ │ │ ├── util │ │ │ │ └── ResultUtil.java │ │ │ └── vo │ │ │ │ └── ResultVO.java │ │ │ └── wiremock │ │ │ └── MockServer.java │ └── resources │ │ ├── application.yml │ │ ├── resources │ │ ├── demo-binding.html │ │ ├── demo-signIn.html │ │ ├── demo-signUp.html │ │ ├── error │ │ │ ├── 401.html │ │ │ ├── 403.html │ │ │ ├── 404.html │ │ │ └── 500.html │ │ ├── index.html │ │ ├── logout.html │ │ └── session-invalid.html │ │ └── wireMock │ │ └── response │ │ ├── order_01.txt │ │ └── order_02.txt │ └── test │ └── java │ └── com │ └── security │ ├── BasicApplicationTest.java │ └── web │ └── controller │ └── DemoUserControllerTest.java ├── permission ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── security │ │ └── permission │ │ ├── authentication │ │ └── RbacUserDetailsService.java │ │ ├── authorize │ │ └── RbacAuthorizeConfigProvider.java │ │ ├── domain │ │ ├── Admin.java │ │ ├── Resource.java │ │ ├── ResourceType.java │ │ ├── Role.java │ │ ├── RoleAdmin.java │ │ └── RoleResource.java │ │ ├── dto │ │ ├── AdminCondition.java │ │ ├── AdminInfo.java │ │ ├── ResourceInfo.java │ │ └── RoleInfo.java │ │ ├── init │ │ ├── AbstractDataInitializer.java │ │ ├── AdminDataInitializer.java │ │ ├── DataInitializer.java │ │ └── SystemDataInitializer.java │ │ ├── repository │ │ ├── AdminRepository.java │ │ ├── PermissionRepository.java │ │ ├── ResourceRepository.java │ │ ├── RoleAdminRepository.java │ │ ├── RoleRepository.java │ │ ├── RoleResourceRepository.java │ │ ├── spec │ │ │ └── AdminSpec.java │ │ └── support │ │ │ ├── AbstractConditionBuilder.java │ │ │ ├── AbstractDomain2InfoConverter.java │ │ │ ├── AbstractEventConditionBuilder.java │ │ │ ├── Domain2InfoConverter.java │ │ │ ├── GenericUtils.java │ │ │ ├── PermissionImplicitNamingStrategy.java │ │ │ ├── PermissionSpecification.java │ │ │ ├── QueryResultConverter.java │ │ │ └── QueryWrapper.java │ │ ├── service │ │ ├── AdminService.java │ │ ├── RbacService.java │ │ ├── ResourceService.java │ │ ├── RoleService.java │ │ └── impl │ │ │ ├── AdminServiceImpl.java │ │ │ ├── RbacServiceImpl.java │ │ │ ├── ResourceServiceImpl.java │ │ │ └── RoleServiceImpl.java │ │ └── web │ │ └── controller │ │ ├── AdminController.java │ │ ├── ResourceController.java │ │ ├── RoleController.java │ │ └── support │ │ └── SimpleResponse.java │ └── resources │ └── resources │ ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ ├── glyphicons-halflings-regular.woff2 │ ├── ionicons.eot │ ├── ionicons.svg │ ├── ionicons.ttf │ └── ionicons.woff │ ├── manage.html │ ├── scripts │ ├── app.js │ ├── commons │ │ ├── admin.js │ │ └── common.js │ ├── framework │ │ ├── angular-animate.min.js │ │ ├── angular-cookies.min.js │ │ ├── angular-deckgrid.js │ │ ├── angular-locale_zh-cn.js │ │ ├── angular-resource.min.js │ │ ├── angular-sanitize.min.js │ │ ├── angular-touch.min.js │ │ ├── angular-tree-control.js │ │ ├── angular-ui-router.min.js │ │ ├── angular.min.js │ │ ├── canvas-to-blob.min.js │ │ ├── city-picker.min.js │ │ ├── iscroll-infinite.js │ │ ├── iscroll-lite.js │ │ ├── iscroll-probe.js │ │ ├── iscroll-zoom.js │ │ ├── iscroll.js │ │ ├── jquery-2.1.4.js │ │ ├── jquery-weui.js │ │ ├── jquery-weui.min.js │ │ ├── jquery.blueimp-gallery.min.js │ │ ├── jquery.min.js │ │ ├── jquery.slimscroll.min.js │ │ ├── load-image.all.min.js │ │ ├── mbdatepicker.js │ │ ├── mobiscroll.custom-2.17.2.min.js │ │ ├── moment.min.js │ │ ├── ng-infinite-scroll.min.js │ │ ├── ng-iscroll.js │ │ ├── plugins │ │ │ ├── advlist │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── anchor │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── autolink │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── autoresize │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── autosave │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── bbcode │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── charmap │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── code │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── codesample │ │ │ │ ├── css │ │ │ │ │ └── prism.css │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── colorpicker │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── contextmenu │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── directionality │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── emoticons │ │ │ │ ├── img │ │ │ │ │ ├── smiley-cool.gif │ │ │ │ │ ├── smiley-cry.gif │ │ │ │ │ ├── smiley-embarassed.gif │ │ │ │ │ ├── smiley-foot-in-mouth.gif │ │ │ │ │ ├── smiley-frown.gif │ │ │ │ │ ├── smiley-innocent.gif │ │ │ │ │ ├── smiley-kiss.gif │ │ │ │ │ ├── smiley-laughing.gif │ │ │ │ │ ├── smiley-money-mouth.gif │ │ │ │ │ ├── smiley-sealed.gif │ │ │ │ │ ├── smiley-smile.gif │ │ │ │ │ ├── smiley-surprised.gif │ │ │ │ │ ├── smiley-tongue-out.gif │ │ │ │ │ ├── smiley-undecided.gif │ │ │ │ │ ├── smiley-wink.gif │ │ │ │ │ └── smiley-yell.gif │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── fullpage │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── fullscreen │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── hr │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── image │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── imagetools │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── importcss │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── insertdatetime │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── layer │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── legacyoutput │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── link │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── lists │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── media │ │ │ │ ├── moxieplayer.swf │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── nonbreaking │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── noneditable │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── pagebreak │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── paste │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── preview │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── print │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── save │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── searchreplace │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── spellchecker │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── tabfocus │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── table │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── template │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── textcolor │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── textpattern │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── visualblocks │ │ │ │ ├── css │ │ │ │ │ └── visualblocks.css │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ ├── visualchars │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ │ └── wordcount │ │ │ │ ├── plugin.js │ │ │ │ └── plugin.min.js │ │ ├── select.js │ │ ├── select.min.js │ │ ├── simplify.js │ │ ├── simplify_dashboard.js │ │ ├── skins │ │ │ └── lightgray │ │ │ │ ├── content.inline.min.css │ │ │ │ ├── content.min.css │ │ │ │ ├── fonts │ │ │ │ ├── tinymce-small.eot │ │ │ │ ├── tinymce-small.svg │ │ │ │ ├── tinymce-small.ttf │ │ │ │ ├── tinymce-small.woff │ │ │ │ ├── tinymce.eot │ │ │ │ ├── tinymce.svg │ │ │ │ ├── tinymce.ttf │ │ │ │ └── tinymce.woff │ │ │ │ ├── img │ │ │ │ ├── anchor.gif │ │ │ │ ├── loader.gif │ │ │ │ ├── object.gif │ │ │ │ └── trans.gif │ │ │ │ ├── skin.ie7.min.css │ │ │ │ └── skin.min.css │ │ ├── swiper-3.3.1.min.js │ │ ├── themes │ │ │ └── modern │ │ │ │ ├── theme.js │ │ │ │ └── theme.min.js │ │ ├── tinymce.js │ │ ├── tinymce.min.js │ │ ├── toastr.min.js │ │ ├── ui-bootstrap-tpls.min.js │ │ ├── ui-bootstrap.min.js │ │ └── uploader.min.js │ └── platform │ │ ├── controller │ │ ├── adminController.js │ │ ├── resourceController.js │ │ ├── roleController.js │ │ └── sidebarController.js │ │ └── module.js │ ├── styles │ ├── blueimp-gallery.min.css │ ├── bootstrap.min.css │ ├── font-awesome.min.css │ ├── frozen.css │ ├── icons_mobiscroll.eot │ ├── icons_mobiscroll.svg │ ├── icons_mobiscroll.ttf │ ├── icons_mobiscroll.woff │ ├── images │ │ └── spinner.gif │ ├── ionicons.min.css │ ├── jquery.fileupload-ui.css │ ├── jquery.fileupload.css │ ├── main.css │ ├── mbdatepicker.css │ ├── mobiscroll.custom-2.17.2.min.css │ ├── pzx-index.css │ ├── select.min.css │ ├── simplify.min.css │ ├── style.css │ ├── swiper-3.3.1.min.css │ ├── toastr.min.css │ ├── tree-control-attribute.css │ └── tree-control.css │ └── views │ ├── commons │ ├── confirm.html │ ├── partials │ │ ├── content.html │ │ ├── offsidebar.html │ │ ├── sidebar.html │ │ └── top-navbar.html │ └── password.html │ ├── main.html │ └── platform │ ├── adminForm.html │ ├── adminManage.html │ ├── resourceForm.html │ ├── resourceManage.html │ ├── roleForm.html │ └── roleManage.html ├── pom.xml ├── springSecurity基本原理.md └── 坑大全.md /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/dictionaries/sunlin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | app 7 | 8 | 9 | main 10 | com.91.security 11 | 1.0.0-SNAPSHOT 12 | 13 | 14 | 15 | 16 | com.91.security 17 | core 18 | ${module.security.version} 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/security/app/authentication/openid/OpenIdAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | package com.security.app.authentication.openid; 2 | 3 | import java.util.Collection; 4 | import org.springframework.security.authentication.AbstractAuthenticationToken; 5 | import org.springframework.security.core.GrantedAuthority; 6 | import org.springframework.security.core.SpringSecurityCoreVersion; 7 | /** 8 | * 用来封装请求认证令牌的数据(openid + providerId) 9 | * @author sca 10 | */ 11 | public class OpenIdAuthenticationToken extends AbstractAuthenticationToken { 12 | 13 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 14 | 15 | private final Object principal; 16 | private String providerId; 17 | 18 | public OpenIdAuthenticationToken(String openId,String providerId) { 19 | super(null); 20 | this.principal = openId; 21 | this.providerId = providerId; 22 | setAuthenticated(false); 23 | } 24 | 25 | public OpenIdAuthenticationToken(Object principal,Collection authorities) { 26 | super(authorities); 27 | this.principal = principal; 28 | // must user super, as we override 29 | super.setAuthenticated(true); 30 | } 31 | 32 | @Override 33 | public Object getCredentials() { 34 | 35 | return null; 36 | } 37 | 38 | @Override 39 | public Object getPrincipal() { 40 | 41 | return this.principal; 42 | } 43 | 44 | public String getProviderId() { 45 | return providerId; 46 | } 47 | 48 | @Override 49 | public void setAuthenticated(boolean isAuthenticated) { 50 | if(isAuthenticated) { 51 | throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead "); 52 | } 53 | 54 | super.setAuthenticated(isAuthenticated); 55 | } 56 | 57 | @Override 58 | public void eraseCredentials() { 59 | super.eraseCredentials(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/security/app/authentication/openid/impl/AppSocialAuthenticationFilterPostProcessor.java: -------------------------------------------------------------------------------- 1 | package com.security.app.authentication.openid.impl; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 5 | import org.springframework.social.security.SocialAuthenticationFilter; 6 | import org.springframework.stereotype.Component; 7 | import com.security.core.social.interfaces.SocialAuthenticationFilterPostProcessor; 8 | 9 | /** 10 | * @author sca 11 | * 指定app社交登录成功后的处理逻辑 12 | * 为什么要特殊指定: 13 | * 1、浏览器支持session(JSESSIONID)存储,而app不支持,只能使用token 14 | * 2、security-core中的社交配置类LocalSpringSocialConfigure中的 15 | * SocialAuthenticationFilter调用的还是springBoot默认的认证成功后处理器,并不会返给app需要的token 16 | * 而是还会像浏览器社交登录一样跳转到其他路径上。 17 | */ 18 | @Component 19 | public class AppSocialAuthenticationFilterPostProcessor implements SocialAuthenticationFilterPostProcessor { 20 | 21 | @Autowired 22 | private AuthenticationSuccessHandler ownAuthenticationSuccessHandler; 23 | 24 | @Override 25 | public void process(SocialAuthenticationFilter socialAuthenticationFilter) { 26 | socialAuthenticationFilter.setAuthenticationSuccessHandler(ownAuthenticationSuccessHandler); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/security/app/controller/AppSecurityController.java: -------------------------------------------------------------------------------- 1 | package com.security.app.controller; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.social.connect.Connection; 6 | import org.springframework.social.connect.web.ProviderSignInUtils; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.ResponseStatus; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import org.springframework.web.context.request.ServletWebRequest; 11 | import com.security.app.social.AppSignUpUtils; 12 | import com.security.core.social.AbstractSocialController; 13 | import com.security.core.support.SocialUserInfo; 14 | import com.security.core.properties.SecurityConstants; 15 | import org.springframework.http.HttpStatus; 16 | 17 | /** 18 | * @Description 19 | * @Author sca 20 | * @Date 2019-07-24 11:32 21 | **/ 22 | @RestController 23 | public class AppSecurityController extends AbstractSocialController { 24 | 25 | @Autowired 26 | private ProviderSignInUtils providerSignInUtils; 27 | 28 | @Autowired 29 | private AppSignUpUtils appSignUpUtils; 30 | 31 | /** 32 | * 33 | * @param request 34 | * @return 35 | * app不支持为什么还在用providerSignInUtils 36 | * 答:因为每次请求都会新建一个session,但第二请求会覆盖上一个请求session 37 | * 所以数据还是要先从session中取出来放入redis中 38 | * 39 | * 结果:返回给app 401状态码,引导用户跳转到app注册页 40 | * 41 | */ 42 | @GetMapping(SecurityConstants.DEFAULT_SOCIAL_USER_INFO_URL) 43 | @ResponseStatus(HttpStatus.UNAUTHORIZED) 44 | public SocialUserInfo getSocialUserInfo(HttpServletRequest request) { 45 | Connection connection = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request)); 46 | appSignUpUtils.saveConnectionData(new ServletWebRequest(request), connection.createData()); 47 | return buildSocialUserInfo(connection); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/security/app/exception/AppException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.app.exception; 5 | 6 | import org.springframework.security.core.SpringSecurityCoreVersion; 7 | 8 | /** 9 | * @author sca 10 | * 11 | */ 12 | public class AppException extends RuntimeException { 13 | 14 | /** 15 | * 16 | */ 17 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 18 | 19 | public AppException(String msg) { 20 | super(msg); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/security/app/jwt/LocalJwtTokenEnhancer.java: -------------------------------------------------------------------------------- 1 | package com.security.app.jwt; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; 7 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 8 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 9 | import org.springframework.security.oauth2.provider.token.TokenEnhancer; 10 | import org.springframework.stereotype.Component; 11 | 12 | /** 13 | * @author sca 14 | * Token增强器:改造令牌,添加一些自定义的信息 15 | */ 16 | @Component 17 | public class LocalJwtTokenEnhancer implements TokenEnhancer { 18 | 19 | /** 20 | * 当组装好OAuth2Authentication去生成最终的token时 21 | * 调用生成方法:(私有:无法覆盖)private createAccessToken(..){ 22 | * DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString()); 23 | * ... 24 | * return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token,OAuth2Authentication) : token 25 | * } 26 | * 所以只能通过增强器来改变生成Token内容 27 | */ 28 | @Override 29 | public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { 30 | 31 | Map info = new HashMap<>(1); 32 | info.put("company", "91 "); 33 | 34 | /** 35 | * 设置附加信息 36 | */ 37 | ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(info); 38 | return accessToken; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/security/app/social/SpringSocialConfigurePostProcessor.java: -------------------------------------------------------------------------------- 1 | package com.security.app.social; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | import org.springframework.beans.BeansException; 5 | import org.springframework.beans.factory.config.BeanPostProcessor; 6 | 7 | import com.security.core.properties.SecurityConstants; 8 | import com.security.core.social.config.LocalSpringSocialConfigure; 9 | 10 | /** 11 | * @author sca 12 | * spring容器在初始化之前和之后要做的操作 13 | */ 14 | public class SpringSocialConfigurePostProcessor implements BeanPostProcessor { 15 | 16 | /** 17 | * 初始化之前:直接返回bean,不需要做任何操作 18 | */ 19 | @Override 20 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 21 | return bean; 22 | } 23 | 24 | /** 25 | * 初始化后:修改signupUrl 26 | * 认证未成功: 浏览器是跳转到注册页,app需要直接返回Json数据(引导用户注册),所以需要不同的跳转路径 27 | */ 28 | @Override 29 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 30 | 31 | String keyWord = "socialSecurityConfig"; 32 | if(StringUtils.equals(beanName, keyWord)) { 33 | LocalSpringSocialConfigure config = (LocalSpringSocialConfigure)bean; 34 | config.signupUrl(SecurityConstants.DEFAULT_SOCIAL_USER_INFO_URL); 35 | return config; 36 | } 37 | return bean; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /browser/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | browser 7 | 8 | main 9 | com.91.security 10 | 1.0.0-SNAPSHOT 11 | 12 | 13 | 14 | 15 | com.91.security 16 | core 17 | ${module.security.version} 18 | 19 | 20 | 21 | 22 | org.springframework.session 23 | spring-session 24 | 1.3.5.RELEASE 25 | 26 | 27 | -------------------------------------------------------------------------------- /browser/src/main/java/com/security/browser/logout/LoginOutSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package com.security.browser.logout; 2 | 3 | import java.io.IOException; 4 | import javax.servlet.ServletException; 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.commons.lang.StringUtils; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; 11 | import com.fasterxml.jackson.databind.ObjectMapper; 12 | import com.security.core.support.SimpleResponse; 13 | 14 | /** 15 | * 能接受到退出成功的请求,并做进一步处理 16 | * @author sca 17 | * 18 | */ 19 | @Slf4j 20 | public class LoginOutSuccessHandler implements LogoutSuccessHandler { 21 | 22 | private String logoutUrl; 23 | 24 | private ObjectMapper objMap = new ObjectMapper(); 25 | 26 | 27 | public LoginOutSuccessHandler(String logoutUrl) { 28 | this.logoutUrl = logoutUrl; 29 | } 30 | 31 | @Override 32 | public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) 33 | throws IOException, ServletException { 34 | log.info("退出成功"); 35 | 36 | if(StringUtils.isBlank(logoutUrl)) { 37 | response.setContentType("application/json;charset=utf-8"); 38 | response.getWriter().write(objMap.writeValueAsString(new SimpleResponse("退出成功。"))); 39 | } else { 40 | response.sendRedirect(logoutUrl); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /browser/src/main/java/com/security/browser/session/ExpiredSessionStrategy.java: -------------------------------------------------------------------------------- 1 | package com.security.browser.session; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 6 | 7 | import org.springframework.security.web.session.SessionInformationExpiredEvent; 8 | import org.springframework.security.web.session.SessionInformationExpiredStrategy; 9 | 10 | import com.security.core.properties.SecurityProperties; 11 | 12 | /** 13 | * 并发登录导致session失效时,默认的处理策略 14 | * 记录session超时的具体原因, 15 | * 例:账号被谁挤下去,几点、IP之类的信息 16 | * @author sca 17 | */ 18 | public class ExpiredSessionStrategy extends AbstractSessionStrategy implements SessionInformationExpiredStrategy { 19 | 20 | public ExpiredSessionStrategy(SecurityProperties securityProperties) { 21 | super(securityProperties); 22 | } 23 | 24 | /** 25 | * 并发登录session超时的处理策略 26 | * 参数:SessionInformationExpiredEvent 能获取session超时事件 27 | */ 28 | @Override 29 | public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { 30 | 31 | onSessionInvalid(event.getRequest(),event.getResponse()); 32 | } 33 | 34 | 35 | @Override 36 | protected boolean isConcurrency() { 37 | 38 | return true; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /browser/src/main/java/com/security/browser/session/InvalidSessionStrategy.java: -------------------------------------------------------------------------------- 1 | package com.security.browser.session; 2 | 3 | import java.io.IOException; 4 | import javax.servlet.ServletException; 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | import com.security.core.properties.SecurityProperties; 8 | 9 | /** 10 | * 默认的session失效处理策略 11 | * @author sca 12 | * 13 | */ 14 | public class InvalidSessionStrategy extends AbstractSessionStrategy 15 | implements org.springframework.security.web.session.InvalidSessionStrategy { 16 | 17 | 18 | public InvalidSessionStrategy(SecurityProperties securityProperties) { 19 | super(securityProperties); 20 | 21 | } 22 | 23 | @Override 24 | public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) 25 | throws IOException, ServletException { 26 | 27 | onSessionInvalid(request, response); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /browser/src/main/java/com/security/browser/session/SessionValidateCodeRepository.java: -------------------------------------------------------------------------------- 1 | package com.security.browser.session; 2 | 3 | import org.springframework.social.connect.web.HttpSessionSessionStrategy; 4 | import org.springframework.social.connect.web.SessionStrategy; 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.web.context.request.ServletWebRequest; 7 | import com.security.core.validator.entity.ValidateCode; 8 | import com.security.core.validator.enums.ValidateCodeType; 9 | import com.security.core.validator.inteface.ValidateCodeRepository; 10 | 11 | /** 12 | * @author sca 13 | */ 14 | @Component 15 | public class SessionValidateCodeRepository implements ValidateCodeRepository { 16 | 17 | /** 18 | * 存入session中code对应key的前缀 19 | */ 20 | String sessionKeyPrefix = "SESSION_KEY_FOR_CODE_"; 21 | 22 | /** 23 | * 操作session的工具类 24 | */ 25 | private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); 26 | 27 | 28 | @Override 29 | public void save(ServletWebRequest request, ValidateCode code, ValidateCodeType codeType) { 30 | 31 | sessionStrategy.setAttribute(request, getSessionKey(request,codeType), code); 32 | } 33 | 34 | @Override 35 | public ValidateCode get(ServletWebRequest request, ValidateCodeType codeType) { 36 | return (ValidateCode) sessionStrategy.getAttribute(request, getSessionKey(request, codeType)); 37 | } 38 | 39 | @Override 40 | public void remove(ServletWebRequest request, ValidateCodeType codeType) { 41 | 42 | sessionStrategy.removeAttribute(request, getSessionKey(request, codeType)); 43 | } 44 | 45 | /** 46 | * 构建验证码放入session时的key 47 | * 48 | * @param request 49 | * @return 50 | */ 51 | private String getSessionKey(ServletWebRequest request, ValidateCodeType validateCodeType) { 52 | return sessionKeyPrefix + validateCodeType.toString().toUpperCase(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /browser/src/main/resources/resources/banding.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 |

标准绑定页面

9 |
10 | 11 |
12 | 13 | -------------------------------------------------------------------------------- /browser/src/main/resources/resources/logout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 |

退出成功

9 | 10 | -------------------------------------------------------------------------------- /browser/src/main/resources/resources/session-invalid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Session失效 6 | 7 | 8 |

安全模块默认的session失效提示页面

9 |

请通过com.security.browser.session.sessionInvalidUrl配置自己的页面URL

10 |

此页面将在3秒后跳转到登录页

11 | 14 | 15 | -------------------------------------------------------------------------------- /browser/src/main/resources/resources/signIn-bak.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 |

标准登录页面

9 |

表单登录

10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
用户名:
密码:
图形验证码: 23 | 24 | 25 |
记住我
34 |
35 | 36 |

短信登录

37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 49 | 50 | 51 | 52 | 53 |
手机号:
短信验证码: 46 | 47 | 发送验证码 48 |
54 |
55 |
56 |

社交登录

57 | QQ登录 58 |      59 | 微信登录 60 | 61 | -------------------------------------------------------------------------------- /browser/src/main/resources/resources/signUp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 |

标准注册页面

9 |

这是系统注册页面,请配置com.security.browser.signUpUrl属性来设置自己的注册页

10 | 11 | -------------------------------------------------------------------------------- /browser/src/main/resources/resources/static/img/icon/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/browser/src/main/resources/resources/static/img/icon/mobile.png -------------------------------------------------------------------------------- /browser/src/main/resources/resources/static/img/icon/qq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/browser/src/main/resources/resources/static/img/icon/qq.png -------------------------------------------------------------------------------- /browser/src/main/resources/resources/static/img/icon/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/browser/src/main/resources/resources/static/img/icon/wechat.png -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/SecurityCoreConfig.java: -------------------------------------------------------------------------------- 1 | package com.security.core; 2 | 3 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import com.security.core.properties.SecurityProperties; 7 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | 10 | /** 11 | * 作用:使配置读取器生效 12 | * core: 1、加载系统配置类,并使配置生效 13 | * 2、app、browser公共方法 14 | * @author sca 15 | * 16 | */ 17 | @Configuration 18 | @EnableConfigurationProperties(SecurityProperties.class) 19 | public class SecurityCoreConfig { 20 | 21 | /** 22 | * 密码的加密解密 23 | * 1、用户登录,UserNamePasswordAuthenticationFilter获取到用户输入的密码, 24 | * 2、通过PasswordEncoder的matches方法同数据库加密保存的密码进行比对 25 | * 3、匹配成功matches()方法返回true 26 | * @return 27 | */ 28 | @Bean 29 | public PasswordEncoder passwordEncoder() { 30 | 31 | return new BCryptPasswordEncoder(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/authencation/AbstractChannelSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.security.core.authencation; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 6 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 7 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 8 | import com.security.core.properties.SecurityConstants; 9 | 10 | /** 11 | * WebSecurityConfigurerAdapter web应用安全适配器 12 | * @author sca 13 | * 14 | */ 15 | public class AbstractChannelSecurityConfig extends WebSecurityConfigurerAdapter { 16 | 17 | @Autowired 18 | private AuthenticationSuccessHandler authenticationSuccessHandler; 19 | 20 | @Autowired 21 | private AuthenticationFailureHandler authenticationFailureHandler; 22 | 23 | protected void applyPasswordAuthenticationConfig(HttpSecurity http) throws Exception { 24 | http 25 | .formLogin() 26 | //自定义登录页面 27 | .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL) 28 | .loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM) 29 | .successHandler(authenticationSuccessHandler) 30 | .failureHandler(authenticationFailureHandler); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/authorize/AuthorizeConfigManager.java: -------------------------------------------------------------------------------- 1 | package com.security.core.authorize; 2 | 3 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 4 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 5 | 6 | /** 7 | * @description: 动态授权提供者的管理接口 8 | * @author: sca 9 | * @create: 2019-08-18 23:21 10 | **/ 11 | public interface AuthorizeConfigManager { 12 | 13 | void config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config); 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/authorize/AuthorizeConfigProvider.java: -------------------------------------------------------------------------------- 1 | package com.security.core.authorize; 2 | 3 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 4 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 5 | 6 | /** 7 | * @description: 动态授权接口 8 | * @author: sca 9 | * @create: 2019-08-18 23:08 10 | **/ 11 | public interface AuthorizeConfigProvider { 12 | 13 | /** 14 | * 授权配置提供者接口 15 | * @param config 16 | * @return 17 | */ 18 | boolean config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config); 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/authorize/BaseAuthorizeConfigManager.java: -------------------------------------------------------------------------------- 1 | package com.security.core.authorize; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.Set; 9 | 10 | /** 11 | * @description: 动态授权提供者的管理接口的实现基类 12 | * @author: sca 13 | * @create: 2019-08-18 23:23 14 | **/ 15 | @Component 16 | public class BaseAuthorizeConfigManager implements AuthorizeConfigManager { 17 | 18 | @Autowired 19 | private Set authorizeConfigProviderSet; 20 | 21 | @Override 22 | public void config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config) { 23 | 24 | for (AuthorizeConfigProvider provider : authorizeConfigProviderSet){ 25 | provider.config(config); 26 | } 27 | config.anyRequest().authenticated(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/authorize/BaseAuthorizeConfigProvider.java: -------------------------------------------------------------------------------- 1 | package com.security.core.authorize; 2 | 3 | import com.security.core.properties.SecurityConstants; 4 | import com.security.core.properties.SecurityProperties; 5 | import org.apache.commons.lang.StringUtils; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * @description: 动态授权接口实现类 13 | * @author: sca 14 | * @create: 2019-08-18 23:15 15 | **/ 16 | @Component 17 | public class BaseAuthorizeConfigProvider implements AuthorizeConfigProvider { 18 | 19 | @Autowired 20 | private SecurityProperties securityProperties; 21 | 22 | @Override 23 | public boolean config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config) { 24 | config.antMatchers( 25 | SecurityConstants.DEFAULT_UNAUTHENTICATION_URL, 26 | SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*", 27 | securityProperties.getBrowser().getLoginPage(), 28 | securityProperties.getBrowser().getSignUp(), 29 | securityProperties.getBrowser().getLoginOutUrl(), 30 | securityProperties.getBrowser().getSession().getSessionInvalidUrl(), 31 | "/user/register") 32 | .permitAll(); 33 | 34 | if (StringUtils.isNotBlank(securityProperties.getBrowser().getLoginOutUrl())) { 35 | config.antMatchers(securityProperties.getBrowser().getLoginOutUrl()).permitAll(); 36 | } 37 | return false; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/BaseSocialProperties.java: -------------------------------------------------------------------------------- 1 | package com.security.core.properties; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @description: 社交配置基类 7 | * @author: sca 8 | * @create: 2019-07-23 15:20 9 | **/ 10 | @Data 11 | public class BaseSocialProperties { 12 | 13 | /** 14 | * Application id. 15 | */ 16 | private String appId; 17 | 18 | /** 19 | * Application secret. 20 | */ 21 | private String appSecret; 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/BrowserProperties.java: -------------------------------------------------------------------------------- 1 | package com.security.core.properties; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Description 7 | * @Author sca 8 | * @Date 2019-08-03 17:55 9 | **/ 10 | @Data 11 | public class BrowserProperties { 12 | 13 | /** 14 | * session管理配置项 15 | */ 16 | private SessionProperties session = new SessionProperties(); 17 | /** 18 | * 指定登录页面 19 | */ 20 | private String loginPage = SecurityConstants.DEFAULT_LOGIN_PAGE_URL; 21 | 22 | /** 23 | * 登出请求路径 24 | */ 25 | private String loginOutUrl; 26 | 27 | /** 28 | * 记住我 功能的有效时间,默认1小时 29 | */ 30 | private int rememberMeSeconds = 3600; 31 | 32 | /** 33 | * 注册页 34 | */ 35 | private String signUp = "/signUp.html"; 36 | 37 | /** 38 | * 登录响应的方式,默认是json 39 | */ 40 | private LoginResponseType loginType = LoginResponseType.JSON; 41 | 42 | /** 43 | * 登录成功后跳转的地址 44 | * 只在signInResponseType为REDIRECT时生效 45 | */ 46 | private String signInSuccessUrl; 47 | /** 48 | * 静态资源不拦截路径 49 | */ 50 | private String staticAvoidPath; 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/ImageCodeProperties.java: -------------------------------------------------------------------------------- 1 | package com.security.core.properties; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Description 7 | * @Author sca 8 | * @Date 2019-08-03 17:58 9 | **/ 10 | @Data 11 | public class ImageCodeProperties { 12 | 13 | private int width; 14 | 15 | private int height; 16 | 17 | private int length = 4; 18 | 19 | private int expireIn = 60; 20 | 21 | private String url; 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/LoginResponseType.java: -------------------------------------------------------------------------------- 1 | package com.security.core.properties; 2 | 3 | /** 4 | * @Description 登录响应类型 5 | * @Author sca 6 | * @Date 2019-07-24 11:38 7 | **/ 8 | public enum LoginResponseType { 9 | 10 | /** 11 | * 跳转 12 | */ 13 | REDIRECT, 14 | 15 | /** 16 | * 返回Json 17 | */ 18 | JSON 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/OAuth2ClientProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.properties; 5 | 6 | import lombok.Data; 7 | 8 | /** 9 | * @author sca 10 | * 认证客户端配置 11 | */ 12 | @Data 13 | public class OAuth2ClientProperties { 14 | 15 | private String clientId; 16 | 17 | private String clientSecret; 18 | 19 | /** 20 | * 令牌有效期 21 | */ 22 | private int accessTokenValiditySeconds; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/OAuth2Properties.java: -------------------------------------------------------------------------------- 1 | package com.security.core.properties; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author sca 9 | * 认证配置 10 | */ 11 | @Data 12 | public class OAuth2Properties { 13 | 14 | private String jwtSigningKey = "txhl"; 15 | 16 | private List clients; 17 | 18 | private List str; 19 | 20 | private String tokenStore = "redis"; 21 | 22 | public List getClients() { 23 | return clients; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/QQProperties.java: -------------------------------------------------------------------------------- 1 | package com.security.core.properties; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author sca 7 | * 这里引用的SocialProperties是social源码包下提供的配置抽象类 8 | */ 9 | @Data 10 | public class QQProperties extends BaseSocialProperties { 11 | 12 | /** 13 | * 服务提供商的默认标识 14 | */ 15 | private String providerId = "qq"; 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/SecurityConstants.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.properties; 5 | 6 | /** 7 | * @author sunLin 8 | * 9 | */ 10 | public interface SecurityConstants { 11 | 12 | /** 13 | * 默认的处理验证码的url前缀 14 | */ 15 | String DEFAULT_VALIDATE_CODE_URL_PREFIX = "/code"; 16 | /** 17 | * 当请求需要身份认证时,默认跳转的url 18 | */ 19 | String DEFAULT_UNAUTHENTICATION_URL = "/auth/require"; 20 | /** 21 | * 默认的用户名密码登录请求处理url 22 | */ 23 | String DEFAULT_LOGIN_PROCESSING_URL_FORM = "/auth/form"; 24 | /** 25 | * 默认的手机验证码登录请求处理url 26 | */ 27 | String DEFAULT_LOGIN_PROCESSING_URL_SMS = "/auth/sms"; 28 | 29 | /** 30 | * 默认的OPENID登录请求处理url 31 | */ 32 | String DEFAULT_SIGN_IN_PROCESSING_URL_OPENID = "/auth/openid"; 33 | 34 | /** 35 | * 默认登录页面 36 | */ 37 | String DEFAULT_LOGIN_PAGE_URL = "/signIn.html"; 38 | /** 39 | * 验证图片验证码时,http请求中默认的携带图片验证码信息的参数的名称 40 | */ 41 | String DEFAULT_PARAMETER_NAME_CODE_IMAGE = "imgCode"; 42 | /** 43 | * 验证短信验证码时,http请求中默认的携带短信验证码信息的参数的名称 44 | */ 45 | String DEFAULT_PARAMETER_NAME_CODE_SMS = "smsCode"; 46 | /** 47 | * 发送短信验证码 或 验证短信验证码时,传递手机号的参数的名称 48 | */ 49 | String DEFAULT_PARAMETER_NAME_TEL = "tel"; 50 | 51 | /** 52 | * openid参数名 53 | */ 54 | String DEFAULT_PARAMETER_NAME_OPENID = "openid"; 55 | /** 56 | * providerId参数名 57 | */ 58 | String DEFAULT_PARAMETER_NAME_PROVIDERID = "providerId"; 59 | /** 60 | * session失效默认的跳转地址 61 | */ 62 | String DEFAULT_SESSION_INVALID_URL = "/session-invalid.html"; 63 | /** 64 | * 获取第三方用户信息的url 65 | */ 66 | String DEFAULT_SOCIAL_USER_INFO_URL = "/social/user"; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/SecurityProperties.java: -------------------------------------------------------------------------------- 1 | package com.security.core.properties; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * @Description 配置类 9 | * @Author sca 10 | * @Date 2019-08-03 17:55 11 | **/ 12 | @Data 13 | @ConfigurationProperties(prefix = "com.security") 14 | public class SecurityProperties { 15 | 16 | /** 17 | * 默认将配置文件中 com.security.browser【prefix + 配置类变量名】 开头的配置读取到BrowserProperties中 18 | */ 19 | private BrowserProperties browser = new BrowserProperties(); 20 | 21 | /** 22 | * 请求配置(url?参数=) > 应用配置 > 默认配置 23 | * 默认将配置文件中 com.security.code【prefix + 配置类变量名】 开头的配置读取到ValidateCodeProperties中 24 | */ 25 | private ValidateCodeProperties code = new ValidateCodeProperties(); 26 | 27 | /** 28 | * 第三方配置 29 | * 默认将配置文件中 com.security.social【prefix + 配置类变量名】 开头的配置读取到SocialProperties中 30 | * @return 31 | */ 32 | private SocialProperties social = new SocialProperties(); 33 | 34 | /** 35 | * 认证配置 36 | * 默认将配置文件中 com.security.oauth【prefix + 配置类变量名】 开头的配置读取到OAuth2Properties中 37 | */ 38 | private OAuth2Properties oauth = new OAuth2Properties(); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/SessionProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.properties; 5 | 6 | import lombok.Data; 7 | 8 | /** 9 | * @author sca 10 | * 11 | */ 12 | @Data 13 | public class SessionProperties { 14 | 15 | /** 16 | * 用户在同一个系统中最大session数,默认:1 17 | */ 18 | private int maximumSessions = 1; 19 | /** 20 | * 同一个账户:达到最大session时,是否阻止新的登录请求 21 | * 默认: false 22 | */ 23 | private boolean maxSessionPreventsLogin; 24 | /** 25 | * session失效时跳转的地址 26 | */ 27 | private String sessionInvalidUrl = SecurityConstants.DEFAULT_SESSION_INVALID_URL; 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/SmsProperties.java: -------------------------------------------------------------------------------- 1 | package com.security.core.properties; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Description 短信配置类 7 | * @Author sca 8 | * @Date 2019-08-03 17:57 9 | **/ 10 | @Data 11 | public class SmsProperties { 12 | 13 | private int length = 6; 14 | 15 | private int expireIn = 60; 16 | 17 | /** 18 | * 需要加入短信验证码验证的路径 19 | */ 20 | private String url; 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/SocialProperties.java: -------------------------------------------------------------------------------- 1 | package com.security.core.properties; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Description 社交属性类 7 | * @Author sca 8 | * @Date 2019-07-24 11:41 9 | **/ 10 | @Data 11 | public class SocialProperties { 12 | 13 | private QQProperties qq = new QQProperties(); 14 | 15 | private WeixinProperties weixin = new WeixinProperties(); 16 | 17 | private String filterProcessUrl = "/auth"; 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/ValidateCodeProperties.java: -------------------------------------------------------------------------------- 1 | package com.security.core.properties; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Description 验证码属性类 7 | * @Author sca 8 | * @Date 2019-07-24 11:45 9 | **/ 10 | @Data 11 | public class ValidateCodeProperties { 12 | 13 | private ImageCodeProperties imgCode; 14 | 15 | private SmsProperties smsCode; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/properties/WeixinProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.properties; 5 | 6 | import lombok.Data; 7 | 8 | /** 9 | * @author sca 10 | * 微信配置项 11 | */ 12 | @Data 13 | public class WeixinProperties extends BaseSocialProperties{ 14 | 15 | /** 16 | * 第三方id,用来决定发起第三方登录的url,默认是 weixin。 17 | */ 18 | private String providerId = "weixin"; 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/service/SuperUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.security.core.service; 2 | 3 | import org.springframework.security.core.userdetails.UserDetails; 4 | import org.springframework.security.core.userdetails.UserDetailsService; 5 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 6 | 7 | /** 8 | * @description: 自定义UserDetailsService 9 | * @author: sca 10 | * @create: 2019-07-28 22:30 11 | **/ 12 | public interface SuperUserDetailsService extends UserDetailsService { 13 | 14 | /** 15 | * 通过手机号获取用户详情 16 | * @param tel 17 | * @return 18 | * @throws UsernameNotFoundException 19 | */ 20 | UserDetails loadUserByTel(String tel) throws UsernameNotFoundException; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/AbstractSocialController.java: -------------------------------------------------------------------------------- 1 | package com.security.core.social; 2 | 3 | import org.springframework.social.connect.Connection; 4 | 5 | import com.security.core.support.SocialUserInfo; 6 | 7 | /** 8 | * @Description 9 | * @Author sca 10 | * @Date 2019-08-03 17:24 11 | **/ 12 | public abstract class AbstractSocialController { 13 | 14 | protected SocialUserInfo buildSocialUserInfo(Connection connection) { 15 | 16 | SocialUserInfo userInfo = new SocialUserInfo(); 17 | userInfo.setProviderId(connection.getKey().getProviderId()); 18 | userInfo.setProviderUserId(connection.getKey().getProviderUserId()); 19 | userInfo.setNickname(connection.getDisplayName()); 20 | userInfo.setHeadimg(connection.getImageUrl()); 21 | return userInfo; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/interfaces/SocialAuthenticationFilterPostProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.social.interfaces; 5 | 6 | import org.springframework.social.security.SocialAuthenticationFilter; 7 | 8 | /** 9 | * @author sca 10 | * 指定社交认证成功后处理器接口 11 | */ 12 | public interface SocialAuthenticationFilterPostProcessor { 13 | 14 | /** 15 | * 社交认证流程处理方法 16 | * @param socialAuthenticationFilter 17 | */ 18 | void process(SocialAuthenticationFilter socialAuthenticationFilter); 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/qq/api/QQ.java: -------------------------------------------------------------------------------- 1 | package com.security.core.social.qq.api; 2 | 3 | import com.security.core.social.qq.entity.QQUserInfo; 4 | 5 | /** 6 | * @Description 7 | * @Author sca 8 | * @Date 2019-08-03 17:28 9 | **/ 10 | public interface QQ { 11 | 12 | /** 13 | * 获取QQ用户信息 14 | * @return 15 | */ 16 | QQUserInfo getQQUserInfo(); 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/qq/connect/QQAdapter.java: -------------------------------------------------------------------------------- 1 | package com.security.core.social.qq.connect; 2 | 3 | import org.springframework.social.connect.ApiAdapter; 4 | import org.springframework.social.connect.Connection; 5 | import org.springframework.social.connect.ConnectionValues; 6 | import org.springframework.social.connect.UserProfile; 7 | import com.security.core.social.qq.api.QQ; 8 | import com.security.core.social.qq.entity.QQUserInfo; 9 | 10 | /** 11 | * 接口ApiAdapter:An adapter that bridges between the uniform {@link Connection} model and a specific provider API model. 12 | * @author sca 13 | * 14 | */ 15 | public class QQAdapter implements ApiAdapter { 16 | 17 | @Override 18 | public UserProfile fetchUserProfile(QQ api) { 19 | 20 | return null; 21 | } 22 | 23 | /** 24 | * 在Connection和API之间起到数据适配的作用 25 | * 通过该方法将服务商(Provider)个性化的数据转换封装到ConnectionValues标准的数据结构中 26 | * @param connectionValues 包含创建Connection所需的数据项 27 | */ 28 | @Override 29 | public void setConnectionValues(QQ api, ConnectionValues connectionValues) { 30 | 31 | QQUserInfo userInfo = api.getQQUserInfo(); 32 | connectionValues.setDisplayName(userInfo.getNickname()); 33 | connectionValues.setImageUrl(userInfo.getFigureurl_qq_1()); 34 | // 服务商用户ID 35 | connectionValues.setProviderUserId(userInfo.getOpenId()); 36 | // 个人主页 37 | connectionValues.setProfileUrl(null); 38 | } 39 | 40 | /** 41 | * 测试当前API是否可用 42 | */ 43 | @Override 44 | public boolean test(QQ api) { 45 | 46 | return true; 47 | } 48 | 49 | /** 50 | * 有个人主页的三方,通过发送消息更新状态 51 | * 例:通过在个人主页发送一条weibo 52 | */ 53 | @Override 54 | public void updateStatus(QQ api, String message) { 55 | // do nothing 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/qq/connect/QQConnectionFactory.java: -------------------------------------------------------------------------------- 1 | package com.security.core.social.qq.connect; 2 | 3 | import org.springframework.social.connect.support.OAuth2ConnectionFactory; 4 | import com.security.core.social.qq.api.QQ; 5 | 6 | /** 7 | * 1、SocialAuthenticationService 会自动加载程序中的 ConnectionFactory(由ServiceProvider和Adapter构成) 8 | * --> 2、服务提供商的信息 -(封装)-> 3、Connection -(封装)-> 4、SocialAuthenticationToken 9 | * --> 5、AuthenticationManager (ProviderManager) 10 | * --> 6、找到合适的AuthenticationProvider处理 :SocialAuthenticationToken 11 | * 而Provider会根据SocialAuthenticationToken里封装的Connection的服务商信息, 12 | * 确定具体使用哪个Repository,到DB查询出UserId 13 | * --> UserId作为参数,调用SocialUserDetailsService去业务系统(Spring-demo MyUserDetailsService) 14 | * 将用户信息查询出来 SocialUserDetails 15 | * 并放入SocialAuthenticationToken,同时标记成已认证状态 16 | * --> 存入 SecurityContext中 最终放入到session中 17 | * @author sca 18 | * 19 | */ 20 | public class QQConnectionFactory extends OAuth2ConnectionFactory { 21 | 22 | /** 23 | * @param providerId 服务提供商的唯一标识 24 | */ 25 | public QQConnectionFactory(String providerId, String appId, String appSecret) { 26 | super(providerId, new QQServiceProvider(appId, appSecret), new QQAdapter()); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/qq/connect/QQServiceProvider.java: -------------------------------------------------------------------------------- 1 | package com.security.core.social.qq.connect; 2 | 3 | import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider; 4 | import com.security.core.social.qq.api.QQ; 5 | import com.security.core.social.qq.api.impl.QQApiImpl; 6 | 7 | /** 8 | * @author sca 9 | */ 10 | public class QQServiceProvider extends AbstractOAuth2ServiceProvider{ 11 | 12 | private String appid; 13 | 14 | /** 15 | * 获取授权码地址 -- 将用户导向认证服务器的路径 16 | */ 17 | private static final String URL_AUTHORIZE = "https://graph.qq.com/oauth2.0/authorize"; 18 | /** 19 | * 申请令牌地址 20 | */ 21 | private static final String URL_ACCESS_TOKEN = "https://graph.qq.com/oauth2.0/token"; 22 | 23 | public QQServiceProvider(String appId,String appSecret) { 24 | super(new QQAuth2Template(appId, appSecret, URL_AUTHORIZE, URL_ACCESS_TOKEN)); 25 | this.appid = appId; 26 | } 27 | 28 | @Override 29 | public QQ getApi(String accessToken) { 30 | 31 | return new QQApiImpl(accessToken, appid); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/qq/entity/QQUserInfo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.social.qq.entity; 5 | 6 | import lombok.Data; 7 | 8 | /** 9 | * @Description 10 | * @Author sca 11 | * @Date 2019-08-03 17:53 12 | **/ 13 | @Data 14 | public class QQUserInfo { 15 | 16 | /** 17 | * 返回码 18 | */ 19 | private String ret; 20 | /** 21 | * 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。 22 | */ 23 | private String msg; 24 | /** 25 | * 26 | */ 27 | private String openId; 28 | /** 29 | * 不知道什么东西,文档上没写,但是实际api返回里有。 30 | */ 31 | private String is_lost; 32 | /** 33 | * 省(直辖市) 34 | */ 35 | private String province; 36 | /** 37 | * 市(直辖市区) 38 | */ 39 | private String city; 40 | /** 41 | * 出生年月 42 | */ 43 | private String year; 44 | /** 45 | * 用户在QQ空间的昵称。 46 | */ 47 | private String nickname; 48 | /** 49 | * 大小为30×30像素的QQ空间头像URL。 50 | */ 51 | private String figureurl; 52 | /** 53 | * 大小为50×50像素的QQ空间头像URL。 54 | */ 55 | private String figureurl_1; 56 | /** 57 | * 大小为100×100像素的QQ空间头像URL。 58 | */ 59 | private String figureurl_2; 60 | /** 61 | * 大小为40×40像素的QQ头像URL。 62 | */ 63 | private String figureurl_qq_1; 64 | /** 65 | * 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100×100的头像,但40×40像素则是一定会有。 66 | */ 67 | private String figureurl_qq_2; 68 | /** 69 | * 性别。 如果获取不到则默认返回”男” 70 | */ 71 | private String gender; 72 | /** 73 | * 标识用户是否为黄钻用户(0:不是;1:是)。 74 | */ 75 | private String is_yellow_vip; 76 | /** 77 | * 标识用户是否为黄钻用户(0:不是;1:是) 78 | */ 79 | private String vip; 80 | /** 81 | * 黄钻等级 82 | */ 83 | private String yellow_vip_level; 84 | /** 85 | * 黄钻等级 86 | */ 87 | private String level; 88 | /** 89 | * 标识是否为年费黄钻用户(0:不是; 1:是) 90 | */ 91 | private String is_yellow_year_vip; 92 | 93 | } 94 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/view/ConnectResultView.java: -------------------------------------------------------------------------------- 1 | package com.security.core.social.view; 2 | 3 | import java.util.Map; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | 8 | import org.springframework.web.servlet.view.AbstractView; 9 | 10 | /** 11 | * @author sca 12 | * 绑定结果视图: 绑定操作触发后回执结果(绑定成功/失败) 13 | */ 14 | public class ConnectResultView extends AbstractView { 15 | 16 | 17 | @Override 18 | protected void renderMergedOutputModel(Map model, 19 | HttpServletRequest req, HttpServletResponse rep)throws Exception { 20 | 21 | rep.setContentType("text/html;charset=UTF-8"); 22 | 23 | if(model.get("connections") == null) { 24 | rep.getWriter().write("

解绑成功

"); 25 | } else { 26 | rep.getWriter().write("

绑定成功

"); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/view/ConnectStatusView.java: -------------------------------------------------------------------------------- 1 | package com.security.core.social.view; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | import org.apache.commons.collections.CollectionUtils; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.social.connect.Connection; 12 | import org.springframework.stereotype.Component; 13 | import org.springframework.web.servlet.view.AbstractView; 14 | 15 | /** 16 | * @author sca 17 | * 绑定状态视图,查询已绑定和未绑定的三方都有哪些 18 | */ 19 | @Component("conncet/status") 20 | public class ConnectStatusView extends AbstractView { 21 | 22 | @Autowired 23 | private ObjectMapper objectMapper; 24 | 25 | @Override 26 | protected void renderMergedOutputModel(Map model, 27 | HttpServletRequest req, HttpServletResponse rep)throws Exception { 28 | 29 | //源码: SecuritySocial将查询到的信息put到一个connections(map)中,然后又model.addAttribute("connectionMap",connections); 30 | Map>> connections = (Map>>) model.get("connectionMap"); 31 | 32 | Map result = new HashMap<>(6); 33 | for (String key : connections.keySet()) { 34 | result.put(key, CollectionUtils.isNotEmpty(connections.get(key))); 35 | } 36 | 37 | rep.setContentType("application/json;charset=UTF-8"); 38 | rep.getWriter().write(objectMapper.writeValueAsString(result)); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/wechat/api/WeiXin.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.social.wechat.api; 5 | 6 | /** 7 | * 微信接口 8 | * @author sca 9 | * 10 | */ 11 | public interface WeiXin { 12 | 13 | /** 14 | * 获取微信用户信息 15 | * @param openId 16 | * @return 17 | */ 18 | WeiXinUserInfo getUserInfo(String openId); 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/wechat/api/WeiXinUserInfo.java: -------------------------------------------------------------------------------- 1 | package com.security.core.social.wechat.api; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author sca 7 | */ 8 | @Data 9 | public class WeiXinUserInfo { 10 | 11 | private String openId; 12 | 13 | private String nickName; 14 | 15 | private String language; 16 | 17 | private String sex; 18 | 19 | private String province; 20 | 21 | private String city; 22 | 23 | private String country; 24 | 25 | private String headimgurl; 26 | 27 | private String[] privilege; 28 | 29 | private String unionid; 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/wechat/connect/WeiXinAccessGrant.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.social.wechat.connect; 5 | 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | import org.springframework.security.core.SpringSecurityCoreVersion; 9 | import org.springframework.social.oauth2.AccessGrant; 10 | 11 | /** 12 | * @author sca 13 | * 微信返回信息,于标准OAuth2不同,微信在获取access_token的同时返回openId, 14 | * 并没有单独的通过access_token获取openId的服务 15 | * 16 | * 所以在这继承了标准的AccessGrant,添加了openId字段,作为对“获取access_token”返回信息的封装 17 | */ 18 | public class WeiXinAccessGrant extends AccessGrant { 19 | 20 | /** 21 | * 22 | */ 23 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 24 | 25 | @Getter 26 | @Setter 27 | private String openId; 28 | 29 | public WeiXinAccessGrant(String accessToken) { 30 | super(""); 31 | } 32 | 33 | public WeiXinAccessGrant(String accessToken, String scope, String refreshToken, Long expiresIn) { 34 | super(accessToken, scope, refreshToken, expiresIn); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/wechat/connect/WeiXinServiceProvider.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.social.wechat.connect; 5 | 6 | import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider; 7 | import com.security.core.social.wechat.api.WeiXin; 8 | import com.security.core.social.wechat.api.WeiXinImpl; 9 | 10 | /** 11 | * @author sca 12 | * 微信的OAuth2流程处理器的提供器,供spring social的connect体系调用 13 | */ 14 | public class WeiXinServiceProvider extends AbstractOAuth2ServiceProvider { 15 | 16 | /** 17 | * 微信获取授权码的url 18 | */ 19 | private static final String URL_AUTHORIZE = "https://open.weixin.qq.com/connect/qrconnect"; 20 | /** 21 | * 微信获取accessToken的url 22 | */ 23 | private static final String URL_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token"; 24 | 25 | 26 | public WeiXinServiceProvider(String appId, String appSecret) { 27 | super(new WeiXinTemplate(appId, appSecret, URL_AUTHORIZE, URL_ACCESS_TOKEN)); 28 | } 29 | 30 | @Override 31 | public WeiXin getApi(String accessToken) { 32 | 33 | return new WeiXinImpl(accessToken); 34 | } 35 | 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/social/wechat/connect/WeixinAdapter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.social.wechat.connect; 5 | 6 | import org.springframework.social.connect.ApiAdapter; 7 | import org.springframework.social.connect.ConnectionValues; 8 | import org.springframework.social.connect.UserProfile; 9 | 10 | import com.security.core.social.wechat.api.WeiXin; 11 | import com.security.core.social.wechat.api.WeiXinUserInfo; 12 | 13 | /** 14 | * 将微信 api的数据模型转为spring social的标准模型。 15 | * @author sca 16 | * 17 | */ 18 | public class WeixinAdapter implements ApiAdapter { 19 | 20 | private String openId; 21 | 22 | public WeixinAdapter() { 23 | 24 | } 25 | 26 | public WeixinAdapter(String openId) { 27 | this.openId = openId; 28 | } 29 | 30 | 31 | @Override 32 | public UserProfile fetchUserProfile(WeiXin arg0) { 33 | 34 | return null; 35 | } 36 | 37 | /** 38 | * object trans 39 | */ 40 | @Override 41 | public void setConnectionValues(WeiXin api, ConnectionValues values) { 42 | WeiXinUserInfo profile = api.getUserInfo(openId); 43 | values.setDisplayName(profile.getNickName()); 44 | values.setProviderUserId(profile.getOpenId()); 45 | values.setImageUrl(profile.getHeadimgurl()); 46 | } 47 | 48 | @Override 49 | public boolean test(WeiXin arg0) { 50 | 51 | return true; 52 | } 53 | 54 | @Override 55 | public void updateStatus(WeiXin arg0, String arg1) { 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/support/SimpleResponse.java: -------------------------------------------------------------------------------- 1 | package com.security.core.support; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 请求响应类 7 | * @author sca 8 | * 9 | */ 10 | @Data 11 | public class SimpleResponse { 12 | 13 | public SimpleResponse(Object content){ 14 | this.content = content; 15 | } 16 | 17 | private Object content; 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/support/SocialUserInfo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.support; 5 | 6 | import lombok.Data; 7 | 8 | /** 9 | * 社交用户信息 10 | * @author sca 11 | * 12 | */ 13 | @Data 14 | public class SocialUserInfo { 15 | 16 | private String providerId; 17 | 18 | private String providerUserId; 19 | 20 | private String nickname; 21 | 22 | private String headimg; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/util/Tools.java: -------------------------------------------------------------------------------- 1 | package com.security.core.util; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | import org.springframework.web.context.request.ServletWebRequest; 5 | 6 | /** 7 | * 工具类 8 | * @author sca 9 | * 10 | */ 11 | public class Tools { 12 | 13 | /** 14 | * 通过截取请求URI,判断生成哪个验证码 15 | * @param req 16 | * @return 17 | */ 18 | public static String getProcessorType(ServletWebRequest req) { 19 | return StringUtils.substringAfter(req.getRequest().getRequestURI(), "/code/"); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/config/ValidateCodeBeanConfig.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.config; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import com.security.core.properties.SecurityProperties; 8 | import com.security.core.validator.image.ImageValidateCodeGenerator; 9 | import com.security.core.validator.inteface.ValidateCodeGenerator; 10 | import com.security.core.validator.sms.DefaultSmsCodeSender; 11 | import com.security.core.validator.sms.SmsCodeSender; 12 | 13 | /** 14 | * @Description 验证码配置类 15 | * @Author sca 16 | * @Date 2019-08-03 17:58 17 | **/ 18 | @Configuration 19 | public class ValidateCodeBeanConfig { 20 | 21 | @Autowired 22 | private SecurityProperties sp; 23 | 24 | /** 25 | * 这里有一个思想误区: 26 | * 弄清本地@Bean实例化有无必要的条件:1、实例是更改配置。 2、修改接口实现 27 | * 28 | * 注:默认的实现就是要被覆盖的 29 | * 30 | * @return 31 | */ 32 | @Bean 33 | /** 34 | * 该注解,第一种方式: 再实例化该对象时,首先检查是否已创建名为:imageValidateCodeGenerator的实例。 35 | */ 36 | @ConditionalOnMissingBean(name = "imageValidateCodeGenerator") 37 | public ValidateCodeGenerator imageValidateCodeGenerator () { 38 | ImageValidateCodeGenerator icg = new ImageValidateCodeGenerator(); 39 | icg.setSecurityProperties(sp); 40 | return icg; 41 | } 42 | 43 | @Bean 44 | /** 45 | * 该注解,第二种:查找是否已存在 SmsCodeSender这个接口的实现 46 | */ 47 | @ConditionalOnMissingBean(SmsCodeSender.class) 48 | public SmsCodeSender smsCodeGenerator () { 49 | 50 | return new DefaultSmsCodeSender(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/config/ValidateCodeConfig.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.config; 2 | 3 | import javax.servlet.Filter; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.security.config.annotation.SecurityConfigurerAdapter; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.web.DefaultSecurityFilterChain; 8 | import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * @Description 13 | * @Author sca 14 | * @Date 2019-07-24 15:18 15 | **/ 16 | @Component(value = "validateCodeConfig") 17 | public class ValidateCodeConfig extends SecurityConfigurerAdapter { 18 | 19 | @Autowired 20 | private Filter validateCodeFilter; 21 | 22 | @Override 23 | public void configure(HttpSecurity http) throws Exception { 24 | http.addFilterBefore(validateCodeFilter, AbstractPreAuthenticatedProcessingFilter.class); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/controller/ValidatorCodeController.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.controller; 2 | 3 | import java.util.Map; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import org.springframework.web.context.request.ServletWebRequest; 12 | 13 | import com.security.core.properties.SecurityConstants; 14 | import com.security.core.validator.processor.ValidateCodeProcessor; 15 | 16 | /** 17 | * @Description 验证码请求处理器 18 | * @Author sca 19 | * @Date 2019-08-03 18:00 20 | **/ 21 | @RestController 22 | public class ValidatorCodeController { 23 | 24 | @Autowired 25 | private Map validateCodeProcessors; 26 | 27 | @GetMapping(value = SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX +"/{type}") 28 | public void createCode(HttpServletRequest req,HttpServletResponse rep,@PathVariable String type) throws Exception { 29 | 30 | validateCodeProcessors.get(type + "ValidateCodeProcessor").create(new ServletWebRequest(req,rep)); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/entity/ImageCode.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.entity; 2 | 3 | import lombok.Data; 4 | import org.springframework.security.core.SpringSecurityCoreVersion; 5 | import java.awt.image.BufferedImage; 6 | import java.time.LocalDateTime; 7 | 8 | /** 9 | * 图片验证码 10 | * @author sca 11 | */ 12 | @Data 13 | public class ImageCode extends ValidateCode { 14 | /** 15 | * 16 | */ 17 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;; 18 | 19 | /** 20 | * 图片缓存 21 | */ 22 | private BufferedImage image; 23 | 24 | public ImageCode(BufferedImage image,String code,int expireIn) { 25 | super(code, expireIn); 26 | this.image = image; 27 | } 28 | 29 | public ImageCode(BufferedImage image,String code,LocalDateTime expireTime) { 30 | super(code, expireTime); 31 | this.image = image; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/entity/ValidateCode.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.entity; 2 | 3 | import lombok.Data; 4 | import org.springframework.security.core.SpringSecurityCoreVersion; 5 | 6 | import java.io.Serializable; 7 | import java.time.LocalDateTime; 8 | 9 | /** 10 | * 验证码父类 11 | * @author sca 12 | * 13 | */ 14 | @Data 15 | public class ValidateCode implements Serializable{ 16 | 17 | /** 18 | * 19 | */ 20 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 21 | 22 | /** 23 | * 验证码 24 | */ 25 | private String code; 26 | /** 27 | * 过期时间 28 | */ 29 | private LocalDateTime expireTime; 30 | 31 | public ValidateCode() {} 32 | 33 | public ValidateCode(String code, int expireIn) { 34 | this.code = code; 35 | //当前日期+保存多少秒 36 | this.expireTime = LocalDateTime.now().plusSeconds(expireIn); 37 | } 38 | 39 | public ValidateCode(String code,LocalDateTime expireTime) { 40 | this.code = code; 41 | this.expireTime = expireTime; 42 | } 43 | 44 | public boolean isExpired() { 45 | 46 | return LocalDateTime.now().isAfter(expireTime); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/enums/ValidateCodeType.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.enums; 2 | 3 | import com.security.core.properties.SecurityConstants; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author sca 8 | */ 9 | public enum ValidateCodeType { 10 | 11 | /** 12 | * 短信验证码 13 | */ 14 | SMS("短信"){ 15 | @Override 16 | public String getParamNameValidate() { 17 | return SecurityConstants.DEFAULT_PARAMETER_NAME_CODE_SMS; 18 | } 19 | }, 20 | 21 | /** 22 | * 图片验证码 23 | */ 24 | IMAGE("图片"){ 25 | @Override 26 | public String getParamNameValidate() { 27 | return SecurityConstants.DEFAULT_PARAMETER_NAME_CODE_IMAGE; 28 | } 29 | }; 30 | 31 | ValidateCodeType(String typeName) { 32 | this.typeName = typeName; 33 | } 34 | 35 | @Getter 36 | private String typeName; 37 | 38 | public abstract String getParamNameValidate(); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/exception/ValidateCodeException.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.exception; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | import org.springframework.security.core.SpringSecurityCoreVersion; 5 | 6 | /** 7 | * 验证码异常处理 8 | * @author sca 9 | * 10 | */ 11 | public class ValidateCodeException extends AuthenticationException{ 12 | 13 | 14 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 15 | 16 | public ValidateCodeException(String msg) { 17 | super(msg); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/image/ImageCodeProcessor.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.image; 2 | 3 | import javax.imageio.ImageIO; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.web.context.request.ServletWebRequest; 6 | import com.security.core.validator.entity.ImageCode; 7 | import com.security.core.validator.processor.AbstractValidateCodeProcessor; 8 | 9 | /** 10 | * @Description 11 | * @Author sca 12 | * @Date 2019-08-03 17:58 13 | **/ 14 | @Component(value = "imageValidateCodeProcessor") 15 | public class ImageCodeProcessor extends AbstractValidateCodeProcessor { 16 | 17 | @Override 18 | protected void send(ServletWebRequest request, ImageCode imageCode) throws Exception { 19 | 20 | ImageIO.write(imageCode.getImage(), "JPEG", request.getResponse().getOutputStream()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/inteface/ValidateCodeGenerator.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.inteface; 2 | 3 | import org.springframework.web.context.request.ServletWebRequest; 4 | import com.security.core.validator.entity.ValidateCode; 5 | 6 | /** 7 | * @Description 验证码生成接口 8 | * @Author sca 9 | * @Date 2019-08-03 17:59 10 | **/ 11 | public interface ValidateCodeGenerator { 12 | 13 | /** 14 | * 验证码生成 15 | * @param req 16 | * @return 17 | */ 18 | ValidateCode generate(ServletWebRequest req); 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/inteface/ValidateCodeRepository.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.inteface; 2 | 3 | import org.springframework.web.context.request.ServletWebRequest; 4 | 5 | import com.security.core.validator.entity.ValidateCode; 6 | import com.security.core.validator.enums.ValidateCodeType; 7 | 8 | /** 9 | * 校验码存取器 10 | * @author sca 11 | * 12 | */ 13 | public interface ValidateCodeRepository { 14 | 15 | /** 16 | * 保存验证码 17 | * @param request 18 | * @param code 19 | * @param codeType 20 | */ 21 | void save(ServletWebRequest request, ValidateCode code, ValidateCodeType codeType); 22 | 23 | /** 24 | * 获取验证码 25 | * @param request 26 | * @param codeType 27 | * @return 28 | */ 29 | ValidateCode get(ServletWebRequest request, ValidateCodeType codeType); 30 | 31 | /** 32 | * 移除 33 | * @param request 34 | * @param codeType 35 | */ 36 | void remove(ServletWebRequest request, ValidateCodeType codeType); 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/processor/ValidateCodeProcessor.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.processor; 2 | 3 | import org.springframework.web.context.request.ServletWebRequest; 4 | 5 | /** 6 | * @Description 验证码发送接口 7 | * @Author sca 8 | * @Date 2019-08-03 17:59 9 | **/ 10 | public interface ValidateCodeProcessor { 11 | 12 | /** 13 | * 存入session中code对应key的前缀 14 | */ 15 | String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_"; 16 | 17 | /** 18 | * 生成验证码方法 19 | * ServletWebRequest : spring工具类:封装request和response 20 | * @param request 21 | * @throws Exception 22 | */ 23 | void create(ServletWebRequest request) throws Exception; 24 | 25 | /** 26 | * 校验验证码 27 | * 28 | * @param servletWebRequest 29 | * @throws Exception 30 | */ 31 | void validate(ServletWebRequest servletWebRequest); 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/processor/ValidateCodeProcessorHolder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.core.validator.processor; 5 | 6 | import java.util.Map; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | import com.security.core.validator.enums.ValidateCodeType; 10 | import com.security.core.validator.exception.ValidateCodeException; 11 | 12 | /** 13 | * @author sca 14 | * 15 | */ 16 | @Component 17 | public class ValidateCodeProcessorHolder { 18 | 19 | @Autowired 20 | private Map validateCodeProcessor; 21 | 22 | public ValidateCodeProcessor findProcessorByType(ValidateCodeType type) { 23 | 24 | return findProcessorByType(type.toString().toLowerCase()); 25 | } 26 | 27 | /** 28 | * processor具体处理类 = type + ValidateCodeProcessor.class.getSimpleName() 29 | * @param type 30 | * @return 31 | */ 32 | public ValidateCodeProcessor findProcessorByType(String type) { 33 | String processorName = type + ValidateCodeProcessor.class.getSimpleName(); 34 | ValidateCodeProcessor processor = validateCodeProcessor.get(processorName); 35 | if (processor == null) { 36 | throw new ValidateCodeException("验证码处理器" + processorName + "不存在"); 37 | } 38 | return processor; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/sms/DefaultSmsCodeSender.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.sms; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | /** 6 | * @Description 自定义短信发送类 7 | * @Author sca 8 | * @Date 2019-08-03 17:07 9 | **/ 10 | @Slf4j 11 | public class DefaultSmsCodeSender implements SmsCodeSender { 12 | 13 | @Override 14 | public void send(String tel,String code) { 15 | log.info("mobile number : {} , code: {}" , tel , code); 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/sms/SmsCodeProcessor.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.sms; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.web.bind.ServletRequestUtils; 6 | import org.springframework.web.context.request.ServletWebRequest; 7 | import com.security.core.validator.entity.ValidateCode; 8 | import com.security.core.validator.processor.AbstractValidateCodeProcessor; 9 | 10 | /** 11 | * @Description 短信验证码发送器 12 | * @Author sca 13 | * @Date 2019-08-03 17:55 14 | **/ 15 | @Component(value = "smsValidateCodeProcessor") 16 | public class SmsCodeProcessor extends AbstractValidateCodeProcessor { 17 | 18 | @Autowired 19 | private SmsCodeSender smsCodeSender; 20 | 21 | @Override 22 | protected void send(ServletWebRequest request, ValidateCode validateCode) throws Exception { 23 | String tel = ServletRequestUtils.getRequiredStringParameter( request.getRequest(), "tel"); 24 | smsCodeSender.send(tel, validateCode.getCode()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/sms/SmsCodeSender.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.sms; 2 | 3 | /** 4 | * @Description 发送短信验证码接口 5 | * @Author sca 6 | * @Date 2019-08-03 17:55 7 | **/ 8 | public interface SmsCodeSender { 9 | 10 | /** 11 | * 发送短信验证码 12 | * @param tel 13 | * @param code 14 | */ 15 | void send(String tel, String code); 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/com/security/core/validator/sms/SmsValidateCodeGenerator.java: -------------------------------------------------------------------------------- 1 | package com.security.core.validator.sms; 2 | 3 | import org.apache.commons.lang.RandomStringUtils; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.web.context.request.ServletWebRequest; 7 | 8 | import com.security.core.properties.SecurityProperties; 9 | import com.security.core.validator.entity.ValidateCode; 10 | import com.security.core.validator.inteface.ValidateCodeGenerator; 11 | 12 | /** 13 | * 短信验证码生成 14 | * @author sca 15 | * 16 | */ 17 | @Component(value = "smsValidateCodeGenerator") 18 | public class SmsValidateCodeGenerator implements ValidateCodeGenerator { 19 | 20 | @Autowired 21 | private SecurityProperties securityPro; 22 | 23 | @Override 24 | public ValidateCode generate(ServletWebRequest req) { 25 | String code = RandomStringUtils.randomNumeric(securityPro.getCode().getSmsCode().getLength()); 26 | return new ValidateCode(code, securityPro.getCode().getSmsCode().getExpireIn()); 27 | } 28 | 29 | public SecurityProperties getSecurityPro() { 30 | return securityPro; 31 | } 32 | 33 | public void setSecurityPro(SecurityProperties securityPro) { 34 | this.securityPro = securityPro; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.security; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | /** 8 | * @description: 样例 9 | * @author: sca 10 | * @create: 2019-07-18 18:22 11 | **/ 12 | @SpringBootApplication 13 | @MapperScan(basePackages = "com.security.web.mapper") 14 | public class DemoApplication { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(DemoApplication.class , args); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/exception/UserException.java: -------------------------------------------------------------------------------- 1 | package com.security.exception; 2 | 3 | import com.security.web.enums.ResultEnums; 4 | import lombok.Getter; 5 | import org.springframework.security.core.AuthenticationException; 6 | import org.springframework.security.core.SpringSecurityCoreVersion; 7 | 8 | /** 9 | * @Description 身份认证异常类 10 | * @Author sca 11 | * @Date 2019-08-03 17:58 12 | **/ 13 | @Getter 14 | public class UserException extends AuthenticationException { 15 | 16 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 17 | 18 | private int code; 19 | 20 | public UserException(ResultEnums resultEnums) { 21 | super(resultEnums.getMsg()); 22 | this.code = resultEnums.getCode(); 23 | } 24 | 25 | public UserException(int code, String msg) { 26 | super(msg); 27 | this.code = code; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/handle/ExceptionHandle.java: -------------------------------------------------------------------------------- 1 | package com.security.handle; 2 | 3 | import com.security.exception.UserException; 4 | import com.security.web.enums.ResultEnums; 5 | import com.security.web.util.ResultUtil; 6 | import com.security.web.vo.ResultVO; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.web.bind.annotation.ControllerAdvice; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | import org.springframework.web.bind.annotation.ResponseStatus; 12 | import org.springframework.http.HttpStatus; 13 | 14 | /** 15 | * @Description 16 | * @Author sca 17 | * @Date 2019-08-03 17:57 18 | **/ 19 | @Slf4j 20 | @ControllerAdvice 21 | public class ExceptionHandle { 22 | 23 | @ResponseBody 24 | @ExceptionHandler(Exception.class) 25 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 26 | public ResultVO handlerUserException(Exception e){ 27 | if (e instanceof UserException){ 28 | UserException userException = (UserException) e; 29 | return ResultUtil.error(userException.getCode(), userException.getMessage()); 30 | } 31 | log.error("系统错误: {}" , e); 32 | return ResultUtil.error(ResultEnums.SYSTEM_ERROR.getCode(),ResultEnums.SYSTEM_ERROR.getMsg()); 33 | } 34 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/security/provider/DemoAuthorizeConfigProvider.java: -------------------------------------------------------------------------------- 1 | package com.security.provider; 2 | 3 | import com.security.core.authorize.AuthorizeConfigProvider; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @description: 授权配置提供者 10 | * @author: sca 11 | * @create: 2019-08-18 23:34 12 | **/ 13 | @Component 14 | public class DemoAuthorizeConfigProvider implements AuthorizeConfigProvider { 15 | 16 | 17 | @Override 18 | public boolean config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config) { 19 | 20 | return false; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/service/DemoUserService.java: -------------------------------------------------------------------------------- 1 | package com.security.service; 2 | 3 | /** 4 | * @description: demo User逻辑层 5 | * @author: sca 6 | * @create: 2019-07-19 21:26 7 | **/ 8 | public interface DemoUserService { 9 | 10 | /** 11 | * doSomething 12 | * @param userName 13 | * @return 14 | */ 15 | String doSomething(String userName); 16 | } 17 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/service/impl/DemoUserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.security.service.impl; 2 | 3 | import com.security.service.DemoUserService; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Service; 6 | 7 | /** 8 | * @description: demo user 逻辑层 9 | * @author: sca 10 | * @create: 2019-07-19 21:27 11 | **/ 12 | @Slf4j 13 | @Service 14 | public class DemoUserServiceImpl implements DemoUserService { 15 | 16 | 17 | @Override 18 | public String doSomething(String userName) { 19 | StringBuilder sb = new StringBuilder(); 20 | sb.append("你好,").append(userName).append(". ").append("这里是自定义注解demo版"); 21 | log.info(sb.toString()); 22 | return "access"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/social/ConnectionSignUp.java: -------------------------------------------------------------------------------- 1 | package com.security.social; 2 | 3 | import org.springframework.social.connect.Connection; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * 第三方登录 且是 未注册用户默认自动生成一个userInfo 8 | * @author Administrator 9 | * 10 | */ 11 | @Component 12 | public class ConnectionSignUp implements org.springframework.social.connect.ConnectionSignUp { 13 | 14 | /** 15 | * 当从userConnections表中查询不到,同时ConnectionSignUp不为空 16 | * 且excute(...)返回值不为空,则进行创建 17 | * 18 | * 源码: JdbcUsersConnectionRepository --> findUserIdWithConnection 19 | */ 20 | @Override 21 | public String execute(Connection connection) { 22 | //根据社交用户信息 默认 创建用户,并返回用户唯一标识 23 | return connection.getDisplayName(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/validator/MyConstraint.java: -------------------------------------------------------------------------------- 1 | package com.security.validator; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | import javax.validation.Constraint; 8 | import javax.validation.Payload; 9 | 10 | 11 | /** 12 | * @Target指明该注解可以标注在哪些地方@Target指明该注解可以标注在哪些地方 13 | */ 14 | @Target({ElementType.METHOD, ElementType.FIELD}) 15 | /** 16 | * 指明该注解在运行时发挥作用 17 | */ 18 | @Retention(RetentionPolicy.RUNTIME) 19 | /** 20 | * 指明注解执行校验逻辑的类 21 | */ 22 | @Constraint(validatedBy = MyConstraintValidator.class) 23 | /** 24 | * @Description 25 | * @author sca 26 | * @Date 2019-08-03 17:58 27 | **/ 28 | public @interface MyConstraint { 29 | 30 | String message() default "{org.hibernate.validator.constraints.NotBlank.message}"; 31 | 32 | Class[] groups() default { }; // TODO 33 | 34 | Class[] payload() default { }; // TODO 35 | 36 | } 37 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/validator/MyConstraintValidator.java: -------------------------------------------------------------------------------- 1 | package com.security.validator; 2 | 3 | import javax.validation.ConstraintValidator; 4 | import javax.validation.ConstraintValidatorContext; 5 | import com.security.service.DemoUserService; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | 9 | /** 10 | * @Description 自定义注解,校验类 11 | * @Author sca 12 | * @Date 2019-08-03 17:54 13 | **/ 14 | @Slf4j 15 | public class MyConstraintValidator implements ConstraintValidator { 16 | 17 | @Autowired 18 | private DemoUserService demoUserService; 19 | 20 | /** 21 | * 初始化 22 | */ 23 | @Override 24 | public void initialize(MyConstraint constraintAnnotation) { 25 | log.info("Hi,这里是自定义注解,初始化方法"); 26 | } 27 | 28 | /** 29 | * 校验方法 30 | */ 31 | @Override 32 | public boolean isValid(Object value, ConstraintValidatorContext context) { 33 | log.info("isValid value: {} , context: {}" , value,context); 34 | demoUserService.doSomething("Tom"); 35 | log.info("Hi, 这里是自定义注解处理方法,收到的属性值为: " + value); 36 | return false; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/aspect/TimeAspect.java: -------------------------------------------------------------------------------- 1 | package com.security.web.aspect; 2 | 3 | import java.util.Date; 4 | 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.aspectj.lang.ProceedingJoinPoint; 7 | import org.aspectj.lang.annotation.Around; 8 | import org.aspectj.lang.annotation.Aspect; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * 注解丰富,拦截到控制类,拦截粒度更精确,能获取参数 13 | * 请求拦截优先级:Filter > Interceptor > ControllerAdvice > Aspect > controller 14 | * @author sca 15 | * 16 | */ 17 | @Slf4j 18 | @Aspect 19 | @Component 20 | public class TimeAspect { 21 | 22 | /** 23 | * 第一个 * 是任意返回值 24 | * @param pjp 25 | * @return 26 | * @throws Throwable 27 | */ 28 | @Around("execution(* com.security.web.controller.DemoUserController.*(..))") 29 | public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable { 30 | 31 | log.info("time aspect start"); 32 | 33 | Object[] args = pjp.getArgs(); 34 | for (Object arg : args) { 35 | log.info("aspect get request arg is {} ",arg); 36 | } 37 | 38 | long start = System.currentTimeMillis(); 39 | 40 | // 调用后续处理器 -- 进入被调用方法 41 | Object object = pjp.proceed(); 42 | 43 | log.info("aspect time cosuming: {}" , (System.currentTimeMillis() - start)); 44 | 45 | log.info("aspect time end"); 46 | 47 | return object; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/async/AsyncController.java: -------------------------------------------------------------------------------- 1 | package com.security.web.async; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.commons.lang.RandomStringUtils; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | import org.springframework.web.context.request.async.DeferredResult; 9 | 10 | import io.swagger.annotations.Api; 11 | 12 | /** 13 | * 线程异步处理rest请求 14 | * @author Administrator 15 | * 16 | */ 17 | @Slf4j 18 | @RestController 19 | @Api(tags = "异步处理请求") 20 | public class AsyncController { 21 | 22 | @Autowired 23 | private MockQueue mockQueue; 24 | 25 | @Autowired 26 | private DeferredResultHolder deferredResultHolder; 27 | /** 28 | * Runnable由主线程调起才能使用,不如DeferredResult异步处理Rest服务 29 | * @return 30 | * @throws Exception 31 | */ 32 | /*@RequestMapping(value = "/order") 33 | public Callable order() throws Exception { 34 | logger.info("主线程开始"); 35 | // 在spring管理的线程中单开一个线程 36 | Callable result = new Callable() { 37 | @Override 38 | public String call() throws Exception { 39 | logger.info("副线程开始"); 40 | Thread.sleep(1000); 41 | logger.info("副线程结束"); 42 | return "success"; 43 | } 44 | }; 45 | logger.info("主线程结束"); 46 | return result; 47 | }*/ 48 | 49 | /** 50 | * Deferred实现下订单 51 | * @return 52 | * @throws Exception 53 | */ 54 | @RequestMapping(value = "/order") 55 | public DeferredResult order() throws Exception { 56 | log.info("主线程开始"); 57 | 58 | String orderNum = RandomStringUtils.randomNumeric(8); 59 | mockQueue.setPlaceOrder(orderNum); 60 | 61 | DeferredResult result = new DeferredResult<>(); 62 | deferredResultHolder.getMap().put(orderNum, result); 63 | log.info("主线程结束"); 64 | return result; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/async/DeferredResultHolder.java: -------------------------------------------------------------------------------- 1 | package com.security.web.async; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.context.request.async.DeferredResult; 10 | 11 | /** 12 | * 介于 “接收请求线程” 和 “接收响应线程” 中间 13 | * 用来传递 DeferredResult 的对象 (类似于线程间对象共享) 14 | * 15 | * 例: 下订单 16 | * 1、 请求经由 应用1的 "线程1",插入到消息队列。("线程1"释放,继续接收新的请求) 17 | * 2、应用2监听并处理消息。 18 | * 3、应用2处理完将回执给消息队列 19 | * 4、应用1的 "线程2"监听线程消息处理结果,并返回响应 20 | * @author Administrator 21 | * 22 | */ 23 | @Component 24 | public class DeferredResultHolder { 25 | 26 | /** 27 | * key : value == 订单号 : 处理结果 28 | */ 29 | @Getter 30 | @Setter 31 | private Map> map = new HashMap>(); 32 | 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/async/MockQueue.java: -------------------------------------------------------------------------------- 1 | package com.security.web.async; 2 | 3 | import lombok.Data; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * 模拟消息队列 9 | * @author Administrator 10 | * 11 | */ 12 | @Slf4j 13 | @Data 14 | @Component 15 | public class MockQueue { 16 | 17 | private String placeOrder; 18 | 19 | private String completeOrder; 20 | 21 | public String getPlaceOrder() { 22 | return placeOrder; 23 | } 24 | 25 | public void setPlaceOrder(String placeOrder) throws Exception { 26 | new Thread(() -> { 27 | log.info("接到下单请求: {}" , placeOrder); 28 | try { 29 | Thread.sleep(1000); 30 | } catch (InterruptedException e) { 31 | e.printStackTrace(); 32 | } 33 | this.completeOrder = placeOrder; 34 | log.info("下单处理完毕:{}" , placeOrder); 35 | }).start(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/async/QueueListener.java: -------------------------------------------------------------------------------- 1 | package com.security.web.async; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.commons.lang.StringUtils; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.ApplicationListener; 7 | import org.springframework.context.event.ContextRefreshedEvent; 8 | 9 | /** 10 | * ContextRefreshedEvent : spring整个容器初始化完成事件 11 | * @author Administrator 12 | * 13 | */ 14 | @Slf4j 15 | public class QueueListener implements ApplicationListener{ 16 | 17 | @Autowired 18 | private MockQueue mockQueue; 19 | 20 | @Autowired 21 | private DeferredResultHolder deferredResultHolder; 22 | 23 | @Override 24 | public void onApplicationEvent(ContextRefreshedEvent event) { 25 | 26 | new Thread(() -> { 27 | while (true) { 28 | if(StringUtils.isNotBlank(mockQueue.getCompleteOrder())) { 29 | 30 | String orderNum = mockQueue.getCompleteOrder(); 31 | log.info("返回订单处理结果: {}" , orderNum); 32 | deferredResultHolder.getMap().get(orderNum).setResult("place order success"); 33 | 34 | mockQueue.setCompleteOrder(null); 35 | } else { 36 | //如果还有正在处理的订单 37 | try { 38 | Thread.sleep(100); 39 | } catch (InterruptedException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | } 44 | }).start(); 45 | 46 | } 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.security.web.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.ApiInfoBuilder; 6 | import springfox.documentation.builders.PathSelectors; 7 | import springfox.documentation.builders.RequestHandlerSelectors; 8 | import springfox.documentation.service.ApiInfo; 9 | import springfox.documentation.service.Contact; 10 | import springfox.documentation.spi.DocumentationType; 11 | import springfox.documentation.spring.web.plugins.Docket; 12 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 13 | 14 | /** 15 | * @Description swagger配置类 16 | * @Author sca 17 | * @Date 2019-08-03 17:57 18 | **/ 19 | @Configuration 20 | @EnableSwagger2 21 | public class SwaggerConfig { 22 | 23 | @Bean 24 | public Docket aipayApi() { 25 | 26 | return new Docket(DocumentationType.SWAGGER_2).groupName("Security安全接口文档") 27 | .apiInfo(apiInfo()) 28 | .select() 29 | .apis(RequestHandlerSelectors.basePackage("com.security.web")) 30 | .paths(PathSelectors.any()).build(); 31 | } 32 | 33 | private ApiInfo apiInfo() { 34 | return new ApiInfoBuilder() 35 | .title("Security安全认证") 36 | .description("安全服务") 37 | .termsOfServiceUrl("http://cheleifeng.com") 38 | .contact(new Contact("同行互助", "http://cheleifeng.com", "740949744@qq.com")) 39 | .version("1.0").build(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.security.web.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import com.security.web.filter.TimeFilter; 6 | import com.security.web.interceptor.TimeInterceptor; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 12 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 13 | 14 | /** 15 | * @Description 配置类 16 | * @Author sca 17 | * @Date 2019-08-03 18:00 18 | **/ 19 | @Configuration 20 | public class WebConfig implements WebMvcConfigurer { 21 | 22 | //spring对异步的支持 ~~~遗留问题(线程池) 23 | // @Override 24 | // public void configureAsyncSupport(AsyncSupportConfigurer configurer) { 25 | // super.configureAsyncSupport(configurer); 26 | // } 27 | 28 | @Autowired 29 | private TimeInterceptor timeInterceptor; 30 | 31 | /** 32 | * 同步注册拦截器 33 | */ 34 | @Override 35 | public void addInterceptors(InterceptorRegistry registry) { 36 | 37 | registry.addInterceptor(timeInterceptor).addPathPatterns("/**").excludePathPatterns() 38 | .excludePathPatterns("/index.html","/*","/resources/*","/loginout"); 39 | } 40 | 41 | /** 42 | * 通过JavaConfig Bean的方式添加第三方Filter Bean到项目容器中 43 | * @return 44 | */ 45 | @Bean 46 | public FilterRegistrationBean timeFilter() { 47 | //Filter注册类 48 | FilterRegistrationBean registrationBean = new FilterRegistrationBean(); 49 | //假设:第三方Filter 没有使用@Component注解 50 | TimeFilter timeFilter = new TimeFilter(); 51 | registrationBean.setFilter(timeFilter); 52 | 53 | //拦截路径集合 54 | List urls = new ArrayList<>(); 55 | urls.add("/*"); 56 | registrationBean.setUrlPatterns(urls); 57 | return registrationBean; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/controller/DemoFileUpload.java: -------------------------------------------------------------------------------- 1 | package com.security.web.controller; 2 | 3 | import com.security.web.entity.FileInfo; 4 | import io.swagger.annotations.Api; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.tomcat.util.http.fileupload.IOUtils; 7 | import org.springframework.web.bind.annotation.*; 8 | import org.springframework.web.multipart.MultipartFile; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.*; 13 | import java.util.Date; 14 | 15 | /** 16 | * @description: 文件上传 17 | * @author: sca 18 | * @create: 2019-07-21 19:34 19 | **/ 20 | @Slf4j 21 | @RestController 22 | @Api(tags = "文件服务") 23 | @RequestMapping(value ="/file") 24 | public class DemoFileUpload { 25 | 26 | static final String FOLDER = "/Users/sunlin/Documents/securitySource"; 27 | @PostMapping 28 | public FileInfo upload(MultipartFile file) throws IllegalStateException, IOException { 29 | log.info("文件名称: {} , 原始文件名: {} , 文件大小: {}" , file.getName(),file.getOriginalFilename(),file.getSize()); 30 | 31 | File localFile = new File(FOLDER,System.currentTimeMillis()+".txt"); 32 | //将传入的file写入到程序本地文件中 33 | file.transferTo(localFile); 34 | 35 | return new FileInfo(localFile.getAbsolutePath()); 36 | } 37 | 38 | @GetMapping(value = "/{id}") 39 | public void downLoad(@PathVariable String id, HttpServletRequest request, HttpServletResponse response) throws IOException { 40 | try ( 41 | InputStream inStream = new FileInputStream(new File(FOLDER,id+".txt")); 42 | OutputStream outputStream = response.getOutputStream(); 43 | ){ 44 | response.setContentType("application/x-download"); 45 | response.addHeader("Content-Disposition", "attachment;filename-test.txt"); 46 | 47 | IOUtils.copy(inStream, outputStream); 48 | outputStream.flush(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/dao/DemoUserDetailsDao.java: -------------------------------------------------------------------------------- 1 | package com.security.web.dao; 2 | 3 | 4 | import com.security.web.entity.User; 5 | 6 | /** 7 | * @description: demo user 查询 8 | * @author: sca 9 | * @create: 2019-07-27 14:57 10 | **/ 11 | public interface DemoUserDetailsDao { 12 | 13 | /** 14 | * 通过ID查询用户详情 15 | * @param userId 16 | * @return 17 | */ 18 | User loadUserDetailsById(String userId); 19 | 20 | /** 21 | * 通过手机号查询用户详情 22 | * @param userId 23 | * @return 24 | */ 25 | User loadUserDetailsByTel(String userId); 26 | } 27 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/dao/impl/DemoUserDetailsDaoImpl.java: -------------------------------------------------------------------------------- 1 | package com.security.web.dao.impl; 2 | 3 | import com.security.web.dao.DemoUserDetailsDao; 4 | import com.security.web.entity.User; 5 | import com.security.web.mapper.UserMapper; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Repository; 8 | 9 | import javax.annotation.Resource; 10 | 11 | /** 12 | * @description: demo user 查询实现层 13 | * @author: sca 14 | * @create: 2019-07-27 15:00 15 | **/ 16 | @Repository 17 | public class DemoUserDetailsDaoImpl implements DemoUserDetailsDao { 18 | 19 | @Resource 20 | private UserMapper userMapper; 21 | 22 | @Override 23 | public User loadUserDetailsById(String userId) { 24 | return userMapper.loadUserDetailsById(userId); 25 | } 26 | 27 | @Override 28 | public User loadUserDetailsByTel(String userId) { 29 | 30 | return userMapper.loadUserDetailsByTel(userId); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/dto/UserQueryCondition.java: -------------------------------------------------------------------------------- 1 | package com.security.web.dto; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.Data; 5 | 6 | /** 7 | * @description: 查询用户条件基类 8 | * @author: sca 9 | * @create: 2019-07-19 14:50 10 | **/ 11 | @Data 12 | public class UserQueryCondition { 13 | 14 | private String username; 15 | 16 | @ApiModelProperty(value = "查询用户年龄起始值") 17 | private int age; 18 | 19 | @ApiModelProperty(value = "查询用户年龄最大值") 20 | private int ageTo; 21 | } 22 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/entity/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.security.web.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * @description: 实体基类 9 | * @author: sca 10 | * @create: 2019-07-27 15:59 11 | **/ 12 | @Data 13 | public class BaseEntity implements Serializable { 14 | 15 | private int id; 16 | 17 | private int updateTime; 18 | 19 | private int createTime; 20 | } 21 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/entity/FileInfo.java: -------------------------------------------------------------------------------- 1 | package com.security.web.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Description 7 | * @Author sca 8 | * @Date 2019-08-03 17:57 9 | **/ 10 | @Data 11 | public class FileInfo { 12 | 13 | public FileInfo(String path) { 14 | this.path = path; 15 | } 16 | 17 | private String path; 18 | 19 | public String getPath() { 20 | return path; 21 | } 22 | 23 | public void setPath(String path) { 24 | this.path = path; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.security.web.entity; 2 | 3 | import javax.validation.constraints.Past; 4 | import lombok.Data; 5 | import com.fasterxml.jackson.annotation.JsonView; 6 | import com.security.validator.MyConstraint; 7 | import org.springframework.security.core.SpringSecurityCoreVersion; 8 | 9 | /** 10 | * @Description 11 | * @Author sca 12 | * @Date 2019-08-03 17:58 13 | **/ 14 | @Data 15 | public class User extends BaseEntity { 16 | 17 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 18 | 19 | public interface UserSimpleView {}; 20 | 21 | public interface UserDetailView extends UserSimpleView {}; 22 | 23 | /** 24 | * 用户名 25 | */ 26 | @MyConstraint(message = "this is test") 27 | @JsonView(UserSimpleView.class) 28 | private String userName; 29 | /** 30 | * 密码 31 | * Hibernate Validator 32 | */ 33 | @JsonView(UserDetailView.class) 34 | private String password; 35 | 36 | /** 37 | * 手机号码 38 | */ 39 | private String tel; 40 | 41 | @Past(message = "生日必须为过去式") 42 | @JsonView(UserSimpleView.class) 43 | private int birthday; 44 | /** 45 | * 锁定状态 46 | */ 47 | private String lockStatus; 48 | /** 49 | * 权限列表 50 | */ 51 | private String authorities; 52 | /** 53 | * 是否启用 54 | */ 55 | private String enabled; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/enums/EnabledStatus.java: -------------------------------------------------------------------------------- 1 | package com.security.web.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @description: 账户的是否启用 7 | * @author: sca 8 | * @create: 2019-07-27 17:38 9 | **/ 10 | @Getter 11 | public enum EnabledStatus { 12 | 13 | /** 14 | * 已启用 15 | */ 16 | ENABLED("0",true), 17 | /** 18 | * 未启用 19 | */ 20 | NOT_ENABLED("1",false), 21 | ; 22 | 23 | private String code; 24 | 25 | private boolean status; 26 | 27 | EnabledStatus(String code, boolean status) { 28 | this.code = code; 29 | this.status = status; 30 | } 31 | 32 | public static boolean getValue(String code){ 33 | for (EnabledStatus status : values()){ 34 | if (status.getCode().equals(code)){ 35 | return status.isStatus(); 36 | } 37 | } 38 | return false; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/enums/LockStatus.java: -------------------------------------------------------------------------------- 1 | package com.security.web.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @description: 账户锁定状态枚举 7 | * @author: sca 8 | * @create: 2019-07-27 17:35 9 | **/ 10 | @Getter 11 | public enum LockStatus { 12 | 13 | /** 14 | * 未锁定 15 | */ 16 | UNLOCKED("0",true), 17 | /** 18 | * 已锁定 19 | */ 20 | LOCKED("1",false) 21 | ; 22 | private String code; 23 | 24 | private boolean status; 25 | 26 | LockStatus(String code, boolean status) { 27 | this.code = code; 28 | this.status = status; 29 | } 30 | 31 | public static boolean getValue(String code){ 32 | for (LockStatus lockStatus : values()){ 33 | if (lockStatus.getCode().equals(code)){ 34 | return lockStatus.isStatus(); 35 | } 36 | } 37 | return false; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/enums/ResultEnums.java: -------------------------------------------------------------------------------- 1 | package com.security.web.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @description: 响应结果枚举 7 | * @author: sca 8 | * @create: 2019-07-27 20:19 9 | **/ 10 | @Getter 11 | public enum ResultEnums { 12 | 13 | /** 14 | * system errors 15 | */ 16 | SUCCESS(200,"成功"), 17 | FAILED(201,"失败"), 18 | UNKONW(202,"未知错误"), 19 | PARAM_ERROR(203,"参数不正确"), 20 | PARAM_CONVERT_ERROR(204,"参数转换异常"), 21 | SYSTEM_ERROR(205,"系统错误"), 22 | /** 23 | * User exception 24 | */ 25 | USER_NOT_EXISTS(401,"用户不存在"), 26 | USER_CREATE_ERROR(402,"用户创建失败"), 27 | ; 28 | 29 | private int code; 30 | 31 | private String msg; 32 | 33 | ResultEnums(Integer code, String msg) { 34 | this.code = code; 35 | this.msg = msg; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/filter/TimeFilter.java: -------------------------------------------------------------------------------- 1 | package com.security.web.filter; 2 | 3 | import java.io.IOException; 4 | import java.util.Date; 5 | import javax.servlet.Filter; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.FilterConfig; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | * 能拿到http请求request和响应response信息 17 | * 但是无法获知处理该http请求的controller及其方法 18 | * 19 | * Filter基于J2EE 20 | * @author sca 21 | * 22 | */ 23 | 24 | /** 25 | * @Description 26 | * @Author sca 27 | * @Date 2019-08-03 17:45 28 | * @Component 注释掉该注解 意在模拟第三方拦截器 配合WebConfig,将第三方拦截器加入到项目中 29 | **/ 30 | public class TimeFilter implements Filter { 31 | 32 | private Logger logger = LoggerFactory.getLogger(getClass()); 33 | 34 | @Override 35 | public void init(FilterConfig filterConfig) throws ServletException { 36 | logger.info("time filter init..."); 37 | } 38 | 39 | @Override 40 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 41 | throws IOException, ServletException { 42 | logger.info("time filter start..."); 43 | long start = System.currentTimeMillis(); 44 | // 45 | chain.doFilter(request, response); 46 | logger.info("time cosuming: {}" , (System.currentTimeMillis() - start)); 47 | logger.info("time filter finish..."); 48 | } 49 | 50 | @Override 51 | public void destroy() { 52 | logger.info("time filter destory..."); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/generator/DemoImageCodeGenerator.java: -------------------------------------------------------------------------------- 1 | package com.security.web.generator; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.web.context.request.ServletWebRequest; 5 | 6 | import com.security.core.validator.entity.ImageCode; 7 | import com.security.core.validator.inteface.ValidateCodeGenerator; 8 | 9 | /** 10 | * 实现验证码生成逻辑的可配置化 11 | * 通过实现接口ValidateCodeGenerator,并通过组件声明的形式,覆盖掉security-core包中默认的生成逻辑 12 | * @author sunLin 13 | * 14 | */ 15 | // @Component("imageValidateCodeGenerator") 16 | public class DemoImageCodeGenerator implements ValidateCodeGenerator { 17 | 18 | 19 | @Override 20 | public ImageCode generate(ServletWebRequest req) { 21 | System.out.println("这是一个很高级的图片验证码"); 22 | return null; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.security.web.mapper; 2 | 3 | import com.security.web.entity.User; 4 | import org.apache.ibatis.annotations.*; 5 | 6 | /** 7 | * @description: demo userDetails mybatis映射文件 8 | * @author: sca 9 | * @create: 2019-07-27 15:02 10 | **/ 11 | public interface UserMapper { 12 | 13 | /** 14 | * 用户ID获取用户详情 15 | * @param userId 16 | * @return 17 | */ 18 | @Select(value = "select * from user where user_name = #{userId}") 19 | @Results({@Result(column = "user_name",property = "userName"), 20 | @Result(column = "lock_status",property = "lockStatus"), 21 | @Result(column = "update_time",property = "updateTime"), 22 | @Result(column = "create_time",property = "createTime")}) 23 | User loadUserDetailsById(String userId); 24 | 25 | /** 26 | * 手机号获取用户详情 27 | * @param tel 28 | * @return 29 | */ 30 | @Select(value = "select * from user where tel = #{tel}") 31 | @Results({@Result(column = "user_name",property = "userName"), 32 | @Result(column = "lock_status",property = "lockStatus"), 33 | @Result(column = "update_time",property = "updateTime"), 34 | @Result(column = "create_time",property = "createTime")}) 35 | User loadUserDetailsByTel(String tel); 36 | } 37 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/util/ResultUtil.java: -------------------------------------------------------------------------------- 1 | package com.security.web.util; 2 | 3 | import com.security.web.enums.ResultEnums; 4 | import com.security.web.vo.ResultVO; 5 | 6 | /** 7 | * @description: 定义响应数据格式工具类 8 | * @author: sca 9 | * @create: 2019-07-27 21:13 10 | **/ 11 | public class ResultUtil { 12 | 13 | public ResultUtil() {} 14 | 15 | public static ResultVO success(Object obj){ 16 | ResultVO resultVO = new ResultVO(); 17 | resultVO.setCode(ResultEnums.SUCCESS.getCode()); 18 | resultVO.setMsg(ResultEnums.SUCCESS.getMsg()); 19 | resultVO.setData(obj); 20 | return resultVO; 21 | } 22 | 23 | public static ResultVO success(){ 24 | return success(null); 25 | } 26 | 27 | public static ResultVO error(int code,String msg){ 28 | ResultVO resultVO = new ResultVO(); 29 | resultVO.setCode(code); 30 | resultVO.setMsg(msg); 31 | resultVO.setData(null); 32 | return resultVO; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/web/vo/ResultVO.java: -------------------------------------------------------------------------------- 1 | package com.security.web.vo; 2 | 3 | import lombok.Data; 4 | import org.springframework.security.core.SpringSecurityCoreVersion; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @description: 结果视图转换类 10 | * @author: sca 11 | * @create: 2019-07-27 21:09 12 | **/ 13 | @Data 14 | public class ResultVO implements Serializable { 15 | 16 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 17 | 18 | /** 19 | * 返回码 20 | */ 21 | private int code; 22 | /** 23 | * 消息 24 | */ 25 | private String msg; 26 | /** 27 | * 数据 28 | */ 29 | private T data; 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/main/java/com/security/wiremock/MockServer.java: -------------------------------------------------------------------------------- 1 | package com.security.wiremock; 2 | 3 | import java.io.IOException; 4 | import com.github.tomakehurst.wiremock.client.WireMock; 5 | import org.apache.commons.lang.StringUtils; 6 | import org.apache.commons.io.FileUtils; 7 | import org.springframework.core.io.ClassPathResource; 8 | 9 | /** 10 | * 伪造rest服务 11 | * @author sca 12 | * 13 | */ 14 | public class MockServer { 15 | 16 | /** 17 | * @param args 18 | * @throws IOException 19 | */ 20 | public static void main(String[] args) throws IOException { 21 | WireMock.configureFor("http://wm.limit-tech.com", 9090); 22 | // 清空之前配置 23 | WireMock.removeAllMappings(); 24 | 25 | mock("order/1","order_01"); 26 | mock("order/2","order_02"); 27 | } 28 | 29 | private static void mock(String url, String filePath) throws IOException { 30 | 31 | ClassPathResource cpr = new ClassPathResource("wireMock/response/" + filePath + ".txt"); 32 | String content = StringUtils.join(FileUtils.readLines(cpr.getFile(),"UTF-8").toArray(),"\n"); 33 | WireMock.stubFor(WireMock.get(WireMock.urlEqualTo("/resource")).willReturn(WireMock.aResponse().withBody(content).withStatus(200))); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /demo/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | driver-class-name: com.mysql.jdbc.Driver 4 | username: sunlin 5 | password: 123456 6 | url: jdbc:mysql://47.94.128.132:3306/basicAuthorization?useUnicode=true&characterEncoding=UTF-8&useSSL=false 7 | jpa: 8 | show-sql: true 9 | open-in-view: true 10 | session: 11 | store-type: none 12 | resources: 13 | static-locations: classpath:/resources/static,classpath:/resources 14 | redis: 15 | port: 6379 16 | host: 47.94.128.132 17 | password: 18 | 19 | server: 20 | port: 8060 21 | 22 | com: 23 | security: 24 | browser: 25 | login-type: JSON 26 | signUp: /demo-signUp.html 27 | loginOutUrl: /logout.html 28 | sessionInvalidUrl: /session-invalid.html 29 | staticAvoidPath: /static/**,/resources/** 30 | code: 31 | imgCode: 32 | length: 4 33 | width: 100 34 | height: 45 35 | url: /user,/user/* 36 | smsCode: 37 | length: 4 38 | expireIn: 600 39 | url: /auth/smsForm 40 | social: 41 | qq: 42 | app-id: 101477439 43 | app-secret: 1c9274f4c91af237a4055680a931cd71 44 | providerId: callback 45 | weixin: 46 | app-id: wxd888ff7ebaff0fdb 47 | app-secret: 771c5f039f324f38a1f18dcb744db897 48 | providerId: weixin 49 | filterProcessUrl: /qqLogin 50 | oauth: 51 | clients[0]: 52 | clientId: txhl 53 | clientSecret: txhlSecret 54 | accessTokenValiditySeconds: 3600 55 | clients[1]: 56 | clientId: test 57 | clientSecret: testSecret 58 | accessTokenValiditySeconds: 3600 59 | tokenStore: redis 60 | storeType: jwt -------------------------------------------------------------------------------- /demo/src/main/resources/resources/demo-binding.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 |

Demo绑定页

9 |
10 | 11 |
12 | 13 | -------------------------------------------------------------------------------- /demo/src/main/resources/resources/demo-signIn.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 |

Demo登录页

9 | 10 | -------------------------------------------------------------------------------- /demo/src/main/resources/resources/demo-signUp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 |

Demo注册页

9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 | 26 |
用户名:
密码:
22 | 23 | 24 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /demo/src/main/resources/resources/error/401.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 6 | 7 | 8 | 认证失败 9 | 10 | -------------------------------------------------------------------------------- /demo/src/main/resources/resources/error/403.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 403 6 | 7 | 8 | 无权访问 9 | 10 | -------------------------------------------------------------------------------- /demo/src/main/resources/resources/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 6 | 7 | 8 | 您所访问的页面不存在 9 | 10 | -------------------------------------------------------------------------------- /demo/src/main/resources/resources/error/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 500 6 | 7 | 8 | 服务器内部错误 9 | 10 | -------------------------------------------------------------------------------- /demo/src/main/resources/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Insert title here 6 | 7 | 8 | index
9 | 10 | 退出 11 | 12 | -------------------------------------------------------------------------------- /demo/src/main/resources/resources/logout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 |

Demo退出成功

9 | 10 | -------------------------------------------------------------------------------- /demo/src/main/resources/resources/session-invalid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Session失效 6 | 7 | 8 |

demo退出成功

9 |

请通过com.security.browser.session.sessionInvalidUrl配置自己的页面URL

10 |

此页面将在3秒后跳转到登录页

11 | 14 | 15 | -------------------------------------------------------------------------------- /demo/src/main/resources/wireMock/response/order_01.txt: -------------------------------------------------------------------------------- 1 | { 2 | "id":1, 3 | "type":"A" 4 | } -------------------------------------------------------------------------------- /demo/src/main/resources/wireMock/response/order_02.txt: -------------------------------------------------------------------------------- 1 | { 2 | "id":2, 3 | "type":"B" 4 | } -------------------------------------------------------------------------------- /demo/src/test/java/com/security/BasicApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.security; 2 | 3 | import org.junit.runner.RunWith; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.junit4.SpringRunner; 6 | 7 | /** 8 | * @description: 测试基础类 9 | * @author: sca 10 | * @create: 2019-07-19 14:13 11 | **/ 12 | @RunWith(SpringRunner.class) 13 | @SpringBootTest 14 | public class BasicApplicationTest { 15 | } 16 | -------------------------------------------------------------------------------- /permission/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | main 7 | com.91.security 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | permission 13 | 14 | 15 | 16 | com.91.security 17 | core 18 | ${module.security.version} 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-jpa 23 | 24 | 25 | joda-time 26 | joda-time 27 | 28 | 29 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/authentication/RbacUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.authentication; 2 | 3 | import com.security.permission.domain.Admin; 4 | import com.security.permission.repository.AdminRepository; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.security.core.userdetails.UserDetailsService; 9 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | /** 14 | * @Description 15 | * @Author sca 16 | * @Date 2019-08-19 11:10 17 | **/ 18 | @Slf4j 19 | //@Component 20 | @Transactional(rollbackFor = {Exception.class}) 21 | public class RbacUserDetailsService implements UserDetailsService { 22 | 23 | @Autowired 24 | private AdminRepository adminRepository; 25 | 26 | @Override 27 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 28 | log.info("表单登录用户名: {}" , username); 29 | Admin admin = adminRepository.findByUsername(username); 30 | admin.getUrls(); 31 | return admin; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/authorize/RbacAuthorizeConfigProvider.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.authorize; 2 | 3 | import com.security.core.authorize.AuthorizeConfigProvider; 4 | import org.springframework.core.annotation.Order; 5 | import org.springframework.http.HttpMethod; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; 8 | import org.springframework.stereotype.Component; 9 | 10 | /** 11 | * @Description 12 | * @Author sca 13 | * @Date 2019-08-19 11:36 14 | **/ 15 | @Component 16 | @Order(Integer.MAX_VALUE) 17 | public class RbacAuthorizeConfigProvider implements AuthorizeConfigProvider { 18 | 19 | /** 20 | * non-Javadoc) 21 | * @see com.security.core.authorize.AuthorizeConfigProvider#config(org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry) 22 | */ 23 | @Override 24 | public boolean config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config) { 25 | config 26 | .antMatchers(HttpMethod.GET, "/fonts/**").permitAll() 27 | .antMatchers(HttpMethod.GET, 28 | "/**/*.html", 29 | "/admin/me", 30 | "/resource").authenticated() 31 | .anyRequest() 32 | .access("@rbacService.hasPermission(request, authentication)"); 33 | return true; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/domain/ResourceType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.permission.domain; 5 | 6 | /** 7 | * @Description 资源类型枚举 8 | * @Author sca 9 | * @Date 2019-08-19 11:32 10 | **/ 11 | public enum ResourceType { 12 | 13 | MENU, 14 | 15 | BUTTON 16 | 17 | } 18 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/domain/Role.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.domain; 2 | 3 | import lombok.Data; 4 | import org.springframework.data.annotation.CreatedDate; 5 | 6 | import javax.persistence.*; 7 | import java.util.Date; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | /** 12 | * @Description 角色信息 13 | * @Author sca 14 | * @Date 2019-08-19 11:33 15 | **/ 16 | @Data 17 | @Entity 18 | public class Role { 19 | 20 | /** 21 | * 数据库表主键 22 | */ 23 | @Id 24 | @GeneratedValue(strategy = GenerationType.IDENTITY) 25 | private Long id; 26 | /** 27 | * 审计日志,记录条目创建时间,自动赋值,不需要程序员手工赋值 28 | */ 29 | @Temporal(TemporalType.TIMESTAMP) 30 | @CreatedDate 31 | private Date createdTime; 32 | /** 33 | * 角色名称 34 | */ 35 | @Column(length = 20, nullable = false) 36 | private String name; 37 | /** 38 | * 角色拥有权限的资源集合 39 | */ 40 | @OneToMany(mappedBy = "role", cascade = CascadeType.REMOVE) 41 | private Set resources = new HashSet<>(); 42 | /** 43 | * 角色的用户集合 44 | */ 45 | @OneToMany(mappedBy = "role", cascade = CascadeType.REMOVE) 46 | private Set admins = new HashSet<>(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/domain/RoleAdmin.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.domain; 2 | 3 | import lombok.Data; 4 | import org.springframework.data.annotation.CreatedDate; 5 | 6 | import javax.persistence.*; 7 | import java.util.Date; 8 | 9 | /** 10 | * @Description 角色用户关系表 11 | * @Author sca 12 | * @Date 2019-08-19 11:33 13 | **/ 14 | @Data 15 | @Entity 16 | public class RoleAdmin { 17 | 18 | /** 19 | * 数据库表主键 20 | */ 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.IDENTITY) 23 | private Long id; 24 | /** 25 | * 审计日志,记录条目创建时间,自动赋值,不需要程序员手工赋值 26 | */ 27 | @Temporal(TemporalType.TIMESTAMP) 28 | @CreatedDate 29 | private Date createdTime; 30 | /** 31 | * 角色 32 | */ 33 | @ManyToOne 34 | private Role role; 35 | /** 36 | * 管理员 37 | */ 38 | @ManyToOne 39 | private Admin admin; 40 | } 41 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/domain/RoleResource.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.domain; 2 | 3 | import lombok.Data; 4 | import org.springframework.data.annotation.CreatedDate; 5 | 6 | import javax.persistence.*; 7 | import java.util.Date; 8 | 9 | /** 10 | * @Description 角色资源关系表 11 | * @Author sca 12 | * @Date 2019-08-19 11:34 13 | **/ 14 | @Data 15 | @Entity 16 | public class RoleResource { 17 | 18 | /** 19 | * 数据库表主键 20 | */ 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.IDENTITY) 23 | private Long id; 24 | /** 25 | * 审计日志,记录条目创建时间,自动赋值,不需要程序员手工赋值 26 | */ 27 | @Temporal(TemporalType.TIMESTAMP) 28 | @CreatedDate 29 | private Date createdTime; 30 | /** 31 | * 角色 32 | */ 33 | @ManyToOne 34 | private Role role; 35 | /** 36 | * 资源 37 | */ 38 | @ManyToOne 39 | private Resource resource; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/dto/AdminCondition.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Description 7 | * @Author sca 8 | * @Date 2019-08-19 11:34 9 | **/ 10 | @Data 11 | public class AdminCondition { 12 | 13 | private String username; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/dto/AdminInfo.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.dto; 2 | 3 | import lombok.Data; 4 | import org.hibernate.validator.constraints.NotBlank; 5 | 6 | import javax.validation.constraints.NotEmpty; 7 | 8 | /** 9 | * @Description 10 | * @Author sca 11 | * @Date 2019-08-19 11:35 12 | **/ 13 | @Data 14 | public class AdminInfo { 15 | 16 | private Long id; 17 | /** 18 | * 角色id 19 | */ 20 | @NotEmpty(message = "角色id不能为空") 21 | private Long roleId; 22 | /** 23 | * 用户名 24 | */ 25 | @NotEmpty(message = "用户名不能为空") 26 | private String username; 27 | 28 | } -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/dto/ResourceInfo.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.dto; 2 | 3 | import com.security.permission.domain.ResourceType; 4 | import lombok.Data; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * @Description 10 | * @Author sca 11 | * @Date 2019-08-19 12:05 12 | **/ 13 | @Data 14 | public class ResourceInfo { 15 | 16 | /** 17 | * 资源ID 18 | * 19 | * @since 1.0.0 20 | */ 21 | private Long id; 22 | /** 23 | * 24 | */ 25 | private Long parentId; 26 | /** 27 | * 资源名 28 | * 29 | * @since 1.0.0 30 | */ 31 | private String name; 32 | /** 33 | * 资源链接 34 | * 35 | * @since 1.0.0 36 | */ 37 | private String link; 38 | /** 39 | * 图标 40 | */ 41 | private String icon; 42 | /** 43 | * 资源类型 44 | */ 45 | private ResourceType type; 46 | /** 47 | * 子节点 48 | */ 49 | private List children = new ArrayList<>(); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/dto/RoleInfo.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Description 7 | * @Author sca 8 | * @Date 2019-08-19 11:36 9 | **/ 10 | @Data 11 | public class RoleInfo { 12 | 13 | private Long id; 14 | 15 | private String name; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/init/AbstractDataInitializer.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.init; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.transaction.annotation.Transactional; 5 | import java.io.IOException; 6 | 7 | /** 8 | * @Description 抽象数据初始化器,所有的数据初始化器应该继承此类 9 | * @Author sca 10 | * @Date 2019-08-19 11:36 11 | **/ 12 | @Slf4j 13 | public abstract class AbstractDataInitializer implements DataInitializer { 14 | 15 | @Override 16 | @Transactional(rollbackFor = {Exception.class}) 17 | public void init() throws Exception { 18 | if(isNeedInit()) { 19 | log.info("使用{}初始化数据" , getClass().getSimpleName()); 20 | doInit(); 21 | log.info("使用{}初始化数据完毕" , getClass().getSimpleName()); 22 | } 23 | } 24 | 25 | /** 26 | * 实际的数据初始化逻辑 27 | * @throws Exception 28 | */ 29 | protected abstract void doInit() throws Exception; 30 | 31 | /** 32 | * 是否执行数据初始化行为 33 | * @return 34 | */ 35 | protected abstract boolean isNeedInit(); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/init/DataInitializer.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.init; 2 | 3 | 4 | /** 5 | * @Description 数据初始化器,设计此接口的目的是封装系统数据的初始化行为,开发人员可以向系统中添加此接口的实现,来增加自定义的数据初始化行为. 6 | * @Author sca 7 | * @Date 2019-08-19 11:41 8 | **/ 9 | public interface DataInitializer { 10 | 11 | /** 12 | * 初始化器的执行顺序,数值越大的初始化器越靠后执行 13 | * 14 | * @return 15 | */ 16 | Integer getIndex(); 17 | 18 | /** 19 | * 初始化数据的方法 20 | * @throws Exception 21 | */ 22 | void init() throws Exception; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/init/SystemDataInitializer.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.init; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.commons.collections.CollectionUtils; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.ApplicationListener; 7 | import org.springframework.context.event.ContextRefreshedEvent; 8 | import org.springframework.stereotype.Component; 9 | import java.util.List; 10 | 11 | /** 12 | * @Description 系统初始化器 13 | * @Author sca 14 | * @Date 2019-08-19 11:41 15 | **/ 16 | @Slf4j 17 | @Component 18 | public class SystemDataInitializer implements ApplicationListener { 19 | 20 | /** 21 | * 系统中所有的{@link DataInitializer}接口实现 22 | */ 23 | @Autowired(required = false) 24 | private List dataInitializers; 25 | 26 | /** 27 | * 循环调用系统中所有的{@link DataInitializer} 28 | */ 29 | @Override 30 | public void onApplicationEvent(ContextRefreshedEvent event) { 31 | 32 | if(CollectionUtils.isNotEmpty(dataInitializers)){ 33 | 34 | dataInitializers.sort((initor1, initor2) -> { 35 | return initor1.getIndex().compareTo(initor2.getIndex()); 36 | }); 37 | 38 | dataInitializers.stream().forEach(dataInitializer -> { 39 | try { 40 | dataInitializer.init(); 41 | } catch (Exception e) { 42 | log.info("系统数据初始化失败: {} : {}" , dataInitializer.getClass().getSimpleName(), e); 43 | } 44 | }); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/AdminRepository.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.repository; 2 | 3 | import com.security.permission.domain.Admin; 4 | import org.springframework.stereotype.Repository; 5 | 6 | /** 7 | * @Description 8 | * @Author sca 9 | * @Date 2019-08-19 13:35 10 | **/ 11 | @Repository 12 | public interface AdminRepository extends PermissionRepository { 13 | 14 | /** 15 | * 根据用户名查询用户 16 | * @param username 17 | * @return 18 | */ 19 | Admin findByUsername(String username); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/PermissionRepository.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.permission.repository; 5 | 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 8 | import org.springframework.data.repository.NoRepositoryBean; 9 | 10 | /** 11 | * @Description 12 | * @Author sca 13 | * @Date 2019-08-19 13:41 14 | **/ 15 | @NoRepositoryBean 16 | public interface PermissionRepository extends JpaRepository, JpaSpecificationExecutor { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/ResourceRepository.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.repository; 2 | 3 | import com.security.permission.domain.Resource; 4 | import org.springframework.stereotype.Repository; 5 | 6 | /** 7 | * @Description 8 | * @Author sca 9 | * @Date 2019-08-19 13:35 10 | **/ 11 | @Repository 12 | public interface ResourceRepository extends PermissionRepository { 13 | 14 | Resource findByName(String name); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/RoleAdminRepository.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.permission.repository; 5 | 6 | import com.security.permission.domain.RoleAdmin; 7 | import org.springframework.stereotype.Repository; 8 | 9 | /** 10 | * @Description 11 | * @Author sca 12 | * @Date 2019-08-19 13:42 13 | **/ 14 | @Repository 15 | public interface RoleAdminRepository extends PermissionRepository { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/RoleRepository.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.permission.repository; 5 | 6 | import com.security.permission.domain.Role; 7 | import org.springframework.stereotype.Repository; 8 | 9 | /** 10 | * @Description 11 | * @Author sca 12 | * @Date 2019-08-19 13:37 13 | **/ 14 | @Repository 15 | public interface RoleRepository extends PermissionRepository { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/RoleResourceRepository.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.permission.repository; 5 | 6 | import com.security.permission.domain.RoleResource; 7 | import org.springframework.stereotype.Repository; 8 | 9 | /** 10 | * @Description 11 | * @Author sca 12 | * @Date 2019-08-19 13:38 13 | **/ 14 | @Repository 15 | public interface RoleResourceRepository extends PermissionRepository { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/spec/AdminSpec.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.repository.spec; 2 | 3 | import com.security.permission.domain.Admin; 4 | import com.security.permission.dto.AdminCondition; 5 | import com.security.permission.repository.support.PermissionSpecification; 6 | import com.security.permission.repository.support.QueryWrapper; 7 | 8 | /** 9 | * @Description 10 | * @Author sca 11 | * @Date 2019-08-19 11:45 12 | **/ 13 | public class AdminSpec extends PermissionSpecification { 14 | 15 | public AdminSpec(AdminCondition condition) { 16 | super(condition); 17 | } 18 | 19 | @Override 20 | protected void addCondition(QueryWrapper queryWrapper) { 21 | addLikeCondition(queryWrapper, "username"); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/support/AbstractDomain2InfoConverter.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.repository.support; 2 | 3 | import org.springframework.beans.BeanUtils; 4 | 5 | 6 | /** 7 | * @Description 8 | * @Author sca 9 | * @Date 2019-08-19 11:49 10 | **/ 11 | public abstract class AbstractDomain2InfoConverter implements Domain2InfoConverter { 12 | 13 | 14 | @Override 15 | public I convert(T domain) { 16 | I info = null; 17 | try { 18 | Class clazz = GenericUtils.getGenericClass(getClass(), 1); 19 | info = clazz.newInstance(); 20 | BeanUtils.copyProperties(domain, info); 21 | doConvert(domain, info); 22 | } catch (Exception e) { 23 | e.printStackTrace(); 24 | } 25 | return info; 26 | } 27 | 28 | protected abstract void doConvert(T domain, I info) throws Exception; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/support/Domain2InfoConverter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.security.permission.repository.support; 5 | 6 | import org.springframework.core.convert.converter.Converter; 7 | 8 | /** 9 | * @Description 10 | * @Author sca 11 | * @Date 2019-08-19 13:31 12 | **/ 13 | public interface Domain2InfoConverter extends Converter { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/support/GenericUtils.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.repository.support; 2 | 3 | import java.lang.reflect.ParameterizedType; 4 | import java.lang.reflect.Type; 5 | 6 | /** 7 | * @Description 泛型工具 8 | * @Author sca 9 | * @Date 2019-08-19 13:31 10 | **/ 11 | public class GenericUtils { 12 | 13 | /** 14 | * 获取目标class的第一个泛型参数的类型 15 | * @param clazz 16 | * @return 17 | */ 18 | public static Class getGenericClass(Class clazz){ 19 | return getGenericClass(clazz, 0); 20 | } 21 | 22 | /** 23 | * 获取目标class的指定位置的泛型参数的类型 24 | * @param clazz 25 | * @param index 泛型参数的位置,第一个参数为0 26 | * @return 27 | */ 28 | public static Class getGenericClass(Class clazz, int index) { 29 | Type t = clazz.getGenericSuperclass(); 30 | if(t instanceof ParameterizedType){ 31 | Type[] params = ((ParameterizedType)t).getActualTypeArguments(); 32 | if(params[index] instanceof ParameterizedType){ 33 | return ((ParameterizedType)params[index]).getRawType().getClass(); 34 | }else{ 35 | return (Class)params[index]; 36 | } 37 | } 38 | throw new RuntimeException("无法获得泛型的类型"); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/support/PermissionImplicitNamingStrategy.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.repository.support; 2 | 3 | import org.hibernate.boot.model.naming.Identifier; 4 | import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl; 5 | import org.hibernate.boot.spi.MetadataBuildingContext; 6 | 7 | /** 8 | * @Description 9 | * @Author sca 10 | * @Date 2019-08-19 13:32 11 | **/ 12 | public class PermissionImplicitNamingStrategy extends ImplicitNamingStrategyJpaCompliantImpl { 13 | 14 | /** 15 | * 16 | */ 17 | private static final long serialVersionUID = 769122522217805485L; 18 | 19 | @Override 20 | protected Identifier toIdentifier(String stringForm, MetadataBuildingContext buildingContext) { 21 | return super.toIdentifier("imooc_"+stringForm.toLowerCase(), buildingContext); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /permission/src/main/java/com/security/permission/repository/support/QueryWrapper.java: -------------------------------------------------------------------------------- 1 | package com.security.permission.repository.support; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import javax.persistence.criteria.CriteriaBuilder; 7 | import javax.persistence.criteria.CriteriaQuery; 8 | import javax.persistence.criteria.Predicate; 9 | import javax.persistence.criteria.Root; 10 | import java.util.List; 11 | 12 | /** 13 | * @Description 包装用于构建JPA动态查询时所需的对象 14 | * @Author sca 15 | * @Date 2019-08-19 13:33 16 | **/ 17 | public class QueryWrapper { 18 | 19 | /** 20 | * @param root 21 | * JPA Root 22 | * @param cb 23 | * JPA CriteriaBuilder 24 | * @param predicates 25 | * JPA Predicate 集合 26 | */ 27 | public QueryWrapper(Root root, CriteriaQuery query, CriteriaBuilder cb, List predicates) { 28 | this.root = root; 29 | this.query = query; 30 | this.cb = cb; 31 | this.predicates = predicates; 32 | } 33 | 34 | /** 35 | * JPA Root 36 | */ 37 | @Getter 38 | @Setter 39 | private Root root; 40 | /** 41 | * JPA CriteriaBuilder 42 | */ 43 | @Getter 44 | @Setter 45 | private CriteriaBuilder cb; 46 | /** 47 | * JPA Predicate 集合 48 | */ 49 | @Getter 50 | private List predicates; 51 | /** 52 | * JPA 查询对象 53 | */ 54 | @Getter 55 | @Setter 56 | private CriteriaQuery query; 57 | 58 | /** 59 | *
60 | 	 *
61 | 	 * 
62 | 	 * @param predicate
63 | 	 * @author jojo 2014-8-12 下午3:12:36
64 | 	 */
65 | 	public void addPredicate(Predicate predicate) {
66 | 		this.predicates.add(predicate);
67 | 	}
68 | }
69 | 


--------------------------------------------------------------------------------
/permission/src/main/java/com/security/permission/service/AdminService.java:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * 
 3 |  */
 4 | package com.security.permission.service;
 5 | 
 6 | import com.security.permission.dto.AdminCondition;
 7 | import com.security.permission.dto.AdminInfo;
 8 | import org.springframework.data.domain.Page;
 9 | import org.springframework.data.domain.Pageable;
10 | 
11 | /**
12 |  * @Description 管理员服务
13 |  * @Author sca
14 |  * @Date 2019-08-19 13:38
15 |  **/
16 | public interface AdminService {
17 | 
18 | 	/**
19 | 	 * 创建管理员
20 | 	 * @param adminInfo
21 | 	 * @return
22 | 	 */
23 | 	AdminInfo create(AdminInfo adminInfo);
24 | 	/**
25 | 	 * 修改管理员
26 | 	 * @param adminInfo
27 | 	 * @return
28 | 	 */
29 | 	AdminInfo update(AdminInfo adminInfo);
30 | 	/**
31 | 	 * 删除管理员
32 | 	 * @param id
33 | 	 */
34 | 	void delete(Long id);
35 | 	/**
36 | 	 * 获取管理员详细信息
37 | 	 * @param id
38 | 	 * @return
39 | 	 */
40 | 	AdminInfo getInfo(Long id);
41 | 	/**
42 | 	 * 分页查询管理员
43 | 	 * @param condition
44 | 	 * @return
45 | 	 */
46 | 	Page query(AdminCondition condition, Pageable pageable);
47 | 
48 | }
49 | 


--------------------------------------------------------------------------------
/permission/src/main/java/com/security/permission/service/RbacService.java:
--------------------------------------------------------------------------------
 1 | package com.security.permission.service;
 2 | 
 3 | import org.springframework.security.core.Authentication;
 4 | 
 5 | import javax.servlet.http.HttpServletRequest;
 6 | 
 7 | /**
 8 |  * @Description
 9 |  * @Author sca
10 |  * @Date 2019-08-19 13:39
11 |  **/
12 | public interface RbacService {
13 | 	
14 | 	boolean hasPermission(HttpServletRequest request, Authentication authentication);
15 | 
16 | }
17 | 


--------------------------------------------------------------------------------
/permission/src/main/java/com/security/permission/service/ResourceService.java:
--------------------------------------------------------------------------------
 1 | package com.security.permission.service;
 2 | 
 3 | import com.security.permission.dto.ResourceInfo;
 4 | 
 5 | /**
 6 |  * @Description 资源服务
 7 |  * @Author sca
 8 |  * @Date 2019-08-19 13:39
 9 |  **/
10 | public interface ResourceService {
11 | 	
12 | 	/**
13 | 	 * 获取资源树
14 | 	 *
15 | 	 * @param userId 用户ID
16 | 	*/
17 | 	ResourceInfo getTree(Long userId);
18 | 
19 | 	/**
20 | 	 * 根据资源ID获取资源信息
21 | 	 *
22 | 	 * @param id 资源ID
23 | 	 * @return ResourceInfo 资源信息
24 | 	*/
25 | 	ResourceInfo getInfo(Long id);
26 | 
27 | 	/**
28 | 	 * 新增资源
29 | 	 *
30 | 	 * @param info 页面传入的资源信息
31 | 	 * @return ResourceInfo 资源信息
32 | 	*/
33 | 	ResourceInfo create(ResourceInfo info);
34 | 	/**
35 | 	 * 更新资源
36 | 	 *
37 | 	 * @param info 页面传入的资源信息
38 | 	 * @return ResourceInfo 资源信息
39 | 	*/
40 | 	ResourceInfo update(ResourceInfo info);
41 | 	/**
42 | 	 * 根据指定ID删除资源信息
43 | 	 *
44 | 	 * @param id 资源ID
45 | 	*/
46 | 	void delete(Long id);
47 | 	/**
48 | 	 * 上移/下移资源
49 | 	 * @param id
50 | 	 * @param up
51 | 	 */
52 | 	Long move(Long id, boolean up);
53 | 
54 | }
55 | 


--------------------------------------------------------------------------------
/permission/src/main/java/com/security/permission/service/RoleService.java:
--------------------------------------------------------------------------------
 1 | package com.security.permission.service;
 2 | 
 3 | 
 4 | import com.security.permission.dto.RoleInfo;
 5 | 
 6 | import java.util.List;
 7 | 
 8 | /**
 9 |  * @Description 角色服务
10 |  * @Author sca
11 |  * @Date 2019-08-19 13:40
12 |  **/
13 | public interface RoleService {
14 | 	
15 | 	/**
16 | 	 * 创建角色
17 | 	 * @param roleInfo
18 | 	 * @return
19 | 	 */
20 | 	RoleInfo create(RoleInfo roleInfo);
21 | 	/**
22 | 	 * 修改角色
23 | 	 * @param roleInfo
24 | 	 * @return
25 | 	 */
26 | 	RoleInfo update(RoleInfo roleInfo);
27 | 	/**
28 | 	 * 删除角色
29 | 	 * @param id
30 | 	 */
31 | 	void delete(Long id);
32 | 	/**
33 | 	 * 获取角色详细信息
34 | 	 * @param id
35 | 	 * @return
36 | 	 */
37 | 	RoleInfo getInfo(Long id);
38 | 	/**
39 | 	 * 查询所有角色
40 | 	 * @return
41 | 	 */
42 | 	List findAll();
43 | 	/**
44 | 	 * @param id
45 | 	 * @return
46 | 	 */
47 | 	String[] getRoleResources(Long id);
48 | 	/**
49 | 	 * @param id
50 | 	 * @param ids
51 | 	 */
52 | 	void setRoleResources(Long id, String ids);
53 | 
54 | }
55 | 


--------------------------------------------------------------------------------
/permission/src/main/java/com/security/permission/service/impl/RbacServiceImpl.java:
--------------------------------------------------------------------------------
 1 | package com.security.permission.service.impl;
 2 | 
 3 | import com.security.permission.domain.Admin;
 4 | import com.security.permission.service.RbacService;
 5 | import org.apache.commons.lang.StringUtils;
 6 | import org.springframework.security.core.Authentication;
 7 | import org.springframework.stereotype.Component;
 8 | import org.springframework.util.AntPathMatcher;
 9 | import javax.servlet.http.HttpServletRequest;
10 | import java.util.Set;
11 | 
12 | /**
13 |  * @Description
14 |  * @Author sca
15 |  * @Date 2019-08-19 13:53
16 |  **/
17 | @Component("rbacService")
18 | public class RbacServiceImpl implements RbacService {
19 | 
20 | 	private AntPathMatcher antPathMatcher = new AntPathMatcher();
21 | 
22 | 	@Override
23 | 	public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
24 | 		Object principal = authentication.getPrincipal();
25 | 
26 | 		boolean hasPermission = false;
27 | 
28 | 		if (principal instanceof Admin) {
29 | 			//如果用户名是admin,就永远返回true
30 | 			if (StringUtils.equals(((Admin) principal).getUsername(), "admin")) {
31 | 				hasPermission = true;
32 | 			} else {
33 | 				// 读取用户所拥有权限的所有URL
34 | 				Set urls = ((Admin) principal).getUrls();
35 | 				for (String url : urls) {
36 | 					if (antPathMatcher.match(url, request.getRequestURI())) {
37 | 						hasPermission = true;
38 | 						break;
39 | 					}
40 | 				}
41 | 			}
42 | 		}
43 | 
44 | 		return hasPermission;
45 | 	}
46 | 
47 | }
48 | 


--------------------------------------------------------------------------------
/permission/src/main/java/com/security/permission/web/controller/support/SimpleResponse.java:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * 
 3 |  */
 4 | package com.security.permission.web.controller.support;
 5 | 
 6 | import lombok.Getter;
 7 | import lombok.Setter;
 8 | 
 9 | /**
10 |  * @Description 
11 |  * @Author sca
12 |  * @Date 2019-08-19 13:40
13 |  **/
14 | public class SimpleResponse {
15 | 	
16 | 	public SimpleResponse(Object content){
17 | 		this.content = content;
18 | 	}
19 | 
20 | 	@Getter
21 | 	@Setter
22 | 	private Object content;
23 | 	
24 | }
25 | 


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/FontAwesome.otf


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/fontawesome-webfont.eot


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/fontawesome-webfont.ttf


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/fontawesome-webfont.woff


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/fontawesome-webfont.woff2


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/glyphicons-halflings-regular.eot


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/glyphicons-halflings-regular.ttf


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/glyphicons-halflings-regular.woff


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/glyphicons-halflings-regular.woff2


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/ionicons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/ionicons.eot


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/ionicons.ttf


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/fonts/ionicons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/fonts/ionicons.woff


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/scripts/app.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | /**
 4 |  * @ngdoc overview
 5 |  * @name testApp
 6 |  * @description
 7 |  * # testApp
 8 |  *
 9 |  * Main module of the application.
10 |  */
11 | //应用主模块
12 | angular.module('adminApp', ['admin']);
13 | 


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/scripts/framework/angular-cookies.min.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  AngularJS v1.5.5
 3 |  (c) 2010-2016 Google, Inc. http://angularjs.org
 4 |  License: MIT
 5 | */
 6 | (function(n,c){'use strict';function l(b,a,g){var d=g.baseHref(),k=b[0];return function(b,e,f){var g,h;f=f||{};h=f.expires;g=c.isDefined(f.path)?f.path:d;c.isUndefined(e)&&(h="Thu, 01 Jan 1970 00:00:00 GMT",e="");c.isString(h)&&(h=new Date(h));e=encodeURIComponent(b)+"="+encodeURIComponent(e);e=e+(g?";path="+g:"")+(f.domain?";domain="+f.domain:"");e+=h?";expires="+h.toUTCString():"";e+=f.secure?";secure":"";f=e.length+1;4096 4096 bytes)!");k.cookie=e}}c.module("ngCookies",["ng"]).provider("$cookies",[function(){var b=this.defaults={};this.$get=["$$cookieReader","$$cookieWriter",function(a,g){return{get:function(d){return a()[d]},getObject:function(d){return(d=this.get(d))?c.fromJson(d):d},getAll:function(){return a()},put:function(d,a,m){g(d,a,m?c.extend({},b,m):b)},putObject:function(d,b,a){this.put(d,c.toJson(b),a)},remove:function(a,k){g(a,void 0,k?c.extend({},b,k):b)}}}]}]);c.module("ngCookies").factory("$cookieStore",
 8 | ["$cookies",function(b){return{get:function(a){return b.getObject(a)},put:function(a,c){b.putObject(a,c)},remove:function(a){b.remove(a)}}}]);l.$inject=["$document","$log","$browser"];c.module("ngCookies").provider("$$cookieWriter",function(){this.$get=l})})(window,window.angular);
 9 | //# sourceMappingURL=angular-cookies.min.js.map
10 | 


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/scripts/framework/canvas-to-blob.min.js:
--------------------------------------------------------------------------------
1 | !function(t){"use strict";var e=t.HTMLCanvasElement&&t.HTMLCanvasElement.prototype,o=t.Blob&&function(){try{return Boolean(new Blob)}catch(t){return!1}}(),n=o&&t.Uint8Array&&function(){try{return 100===new Blob([new Uint8Array(100)]).size}catch(t){return!1}}(),r=t.BlobBuilder||t.WebKitBlobBuilder||t.MozBlobBuilder||t.MSBlobBuilder,a=/^data:((.*?)(;charset=.*?)?)(;base64)?,/,i=(o||r)&&t.atob&&t.ArrayBuffer&&t.Uint8Array&&function(t){var e,i,l,u,b,c,d,B,f;if(e=t.match(a),!e)throw new Error("invalid data URI");for(i=e[2]?e[1]:"text/plain"+(e[3]||";charset=US-ASCII"),l=!!e[4],u=t.slice(e[0].length),b=l?atob(u):decodeURIComponent(u),c=new ArrayBuffer(b.length),d=new Uint8Array(c),B=0;Bt&&(t=0),3==e.nodeType){var n=e.data.length;t>n&&(t=n)}return t}function i(e,t){1!=e.nodeType||e.hasChildNodes()?s.setStart(e,r(e,t)):s.setStartBefore(e)}function o(e,t){1!=e.nodeType||e.hasChildNodes()?s.setEnd(e,r(e,t)):s.setEndAfter(e)}var s,l,c,u,d,f,p,m,h,g;if("A"!=e.selection.getNode().tagName){if(s=e.selection.getRng(!0).cloneRange(),s.startOffset<5){if(m=s.endContainer.previousSibling,!m){if(!s.endContainer.firstChild||!s.endContainer.firstChild.nextSibling)return;m=s.endContainer.firstChild.nextSibling}if(h=m.length,i(m,h),o(m,h),s.endOffset<5)return;l=s.endOffset,u=m}else{if(u=s.endContainer,3!=u.nodeType&&u.firstChild){for(;3!=u.nodeType&&u.firstChild;)u=u.firstChild;3==u.nodeType&&(i(u,0),o(u,u.nodeValue.length))}l=1==s.endOffset?2:s.endOffset-1-t}c=l;do i(u,l>=2?l-2:0),o(u,l>=1?l-1:0),l-=1,g=s.toString();while(" "!=g&&""!==g&&160!=g.charCodeAt(0)&&l-2>=0&&g!=n);s.toString()==n||160==s.toString().charCodeAt(0)?(i(u,l),o(u,c),l+=1):0===s.startOffset?(i(u,0),o(u,c)):(i(u,l),o(u,c)),f=s.toString(),"."==f.charAt(f.length-1)&&o(u,c-1),f=s.toString(),p=f.match(a),p&&("www."==p[1]?p[1]="http://www.":/@$/.test(p[1])&&!/^mailto:/.test(p[1])&&(p[1]="mailto:"+p[1]),d=e.selection.getBookmark(),e.selection.setRng(s),e.execCommand("createlink",!1,p[1]+p[2]),e.selection.moveToBookmark(d),e.nodeChanged())}}var o,a=/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i;return e.settings.autolink_pattern&&(a=e.settings.autolink_pattern),e.on("keydown",function(t){return 13==t.keyCode?r(e):void 0}),tinymce.Env.ie?void e.on("focus",function(){if(!o){o=!0;try{e.execCommand("AutoUrlDetect",!1,!0)}catch(t){}}}):(e.on("keypress",function(n){return 41==n.keyCode?t(e):void 0}),void e.on("keyup",function(t){return 32==t.keyCode?n(e):void 0}))});


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/scripts/framework/plugins/autoresize/plugin.min.js:
--------------------------------------------------------------------------------
1 | tinymce.PluginManager.add("autoresize",function(e){function t(){return e.plugins.fullscreen&&e.plugins.fullscreen.isFullscreen()}function n(r){var a,s,l,c,u,d,f,p,m,h,g,v,b=tinymce.DOM;if(s=e.getDoc()){if(l=s.body,c=s.documentElement,u=i.autoresize_min_height,!l||r&&"setcontent"===r.type&&r.initial||t())return void(l&&c&&(l.style.overflowY="auto",c.style.overflowY="auto"));f=e.dom.getStyle(l,"margin-top",!0),p=e.dom.getStyle(l,"margin-bottom",!0),m=e.dom.getStyle(l,"padding-top",!0),h=e.dom.getStyle(l,"padding-bottom",!0),g=e.dom.getStyle(l,"border-top-width",!0),v=e.dom.getStyle(l,"border-bottom-width",!0),d=l.offsetHeight+parseInt(f,10)+parseInt(p,10)+parseInt(m,10)+parseInt(h,10)+parseInt(g,10)+parseInt(v,10),(isNaN(d)||0>=d)&&(d=tinymce.Env.ie?l.scrollHeight:tinymce.Env.webkit&&0===l.clientHeight?0:l.offsetHeight),d>i.autoresize_min_height&&(u=d),i.autoresize_max_height&&d>i.autoresize_max_height?(u=i.autoresize_max_height,l.style.overflowY="auto",c.style.overflowY="auto"):(l.style.overflowY="hidden",c.style.overflowY="hidden",l.scrollTop=0),u!==o&&(a=u-o,b.setStyle(e.iframeElement,"height",u+"px"),o=u,tinymce.isWebKit&&0>a&&n(r))}}function r(t,i,o){tinymce.util.Delay.setEditorTimeout(e,function(){n({}),t--?r(t,i,o):o&&o()},i)}var i=e.settings,o=0;e.settings.inline||(i.autoresize_min_height=parseInt(e.getParam("autoresize_min_height",e.getElement().offsetHeight),10),i.autoresize_max_height=parseInt(e.getParam("autoresize_max_height",0),10),e.on("init",function(){var t,n;t=e.getParam("autoresize_overflow_padding",1),n=e.getParam("autoresize_bottom_margin",50),t!==!1&&e.dom.setStyles(e.getBody(),{paddingLeft:t,paddingRight:t}),n!==!1&&e.dom.setStyles(e.getBody(),{paddingBottom:n})}),e.on("nodechange setcontent keyup FullscreenStateChanged",n),e.getParam("autoresize_on_init",!0)&&e.on("init",function(){r(20,100,function(){r(5,1e3)})}),e.addCommand("mceAutoResize",n))});


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/scripts/framework/plugins/code/plugin.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * plugin.js
 3 |  *
 4 |  * Released under LGPL License.
 5 |  * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
 6 |  *
 7 |  * License: http://www.tinymce.com/license
 8 |  * Contributing: http://www.tinymce.com/contributing
 9 |  */
10 | 
11 | /*global tinymce:true */
12 | 
13 | tinymce.PluginManager.add('code', function(editor) {
14 | 	function showDialog() {
15 | 		var win = editor.windowManager.open({
16 | 			title: "Source code",
17 | 			body: {
18 | 				type: 'textbox',
19 | 				name: 'code',
20 | 				multiline: true,
21 | 				minWidth: editor.getParam("code_dialog_width", 600),
22 | 				minHeight: editor.getParam("code_dialog_height", Math.min(tinymce.DOM.getViewPort().h - 200, 500)),
23 | 				spellcheck: false,
24 | 				style: 'direction: ltr; text-align: left'
25 | 			},
26 | 			onSubmit: function(e) {
27 | 				// We get a lovely "Wrong document" error in IE 11 if we
28 | 				// don't move the focus to the editor before creating an undo
29 | 				// transation since it tries to make a bookmark for the current selection
30 | 				editor.focus();
31 | 
32 | 				editor.undoManager.transact(function() {
33 | 					editor.setContent(e.data.code);
34 | 				});
35 | 
36 | 				editor.selection.setCursorLocation();
37 | 				editor.nodeChanged();
38 | 			}
39 | 		});
40 | 
41 | 		// Gecko has a major performance issue with textarea
42 | 		// contents so we need to set it when all reflows are done
43 | 		win.find('#code').value(editor.getContent({source_view: true}));
44 | 	}
45 | 
46 | 	editor.addCommand("mceCodeEditor", showDialog);
47 | 
48 | 	editor.addButton('code', {
49 | 		icon: 'code',
50 | 		tooltip: 'Source code',
51 | 		onclick: showDialog
52 | 	});
53 | 
54 | 	editor.addMenuItem('code', {
55 | 		icon: 'code',
56 | 		text: 'Source code',
57 | 		context: 'tools',
58 | 		onclick: showDialog
59 | 	});
60 | });


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/scripts/framework/plugins/code/plugin.min.js:
--------------------------------------------------------------------------------
1 | tinymce.PluginManager.add("code",function(e){function t(){var t=e.windowManager.open({title:"Source code",body:{type:"textbox",name:"code",multiline:!0,minWidth:e.getParam("code_dialog_width",600),minHeight:e.getParam("code_dialog_height",Math.min(tinymce.DOM.getViewPort().h-200,500)),spellcheck:!1,style:"direction: ltr; text-align: left"},onSubmit:function(t){e.focus(),e.undoManager.transact(function(){e.setContent(t.data.code)}),e.selection.setCursorLocation(),e.nodeChanged()}});t.find("#code").value(e.getContent({source_view:!0}))}e.addCommand("mceCodeEditor",t),e.addButton("code",{icon:"code",tooltip:"Source code",onclick:t}),e.addMenuItem("code",{icon:"code",text:"Source code",context:"tools",onclick:t})});


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/scripts/framework/plugins/colorpicker/plugin.min.js:
--------------------------------------------------------------------------------
1 | tinymce.PluginManager.add("colorpicker",function(e){function t(t,n){function r(e){var t=new tinymce.util.Color(e),n=t.toRgb();o.fromJSON({r:n.r,g:n.g,b:n.b,hex:t.toHex().substr(1)}),i(t.toHex())}function i(e){o.find("#preview")[0].getEl().style.background=e}var o=e.windowManager.open({title:"Color",items:{type:"container",layout:"flex",direction:"row",align:"stretch",padding:5,spacing:10,items:[{type:"colorpicker",value:n,onchange:function(){var e=this.rgb();o&&(o.find("#r").value(e.r),o.find("#g").value(e.g),o.find("#b").value(e.b),o.find("#hex").value(this.value().substr(1)),i(this.value()))}},{type:"form",padding:0,labelGap:5,defaults:{type:"textbox",size:7,value:"0",flex:1,spellcheck:!1,onchange:function(){var e,t,n=o.find("colorpicker")[0];return e=this.name(),t=this.value(),"hex"==e?(t="#"+t,r(t),void n.value(t)):(t={r:o.find("#r").value(),g:o.find("#g").value(),b:o.find("#b").value()},n.value(t),void r(t))}},items:[{name:"r",label:"R",autofocus:1},{name:"g",label:"G"},{name:"b",label:"B"},{name:"hex",label:"#",value:"000000"},{name:"preview",type:"container",border:1}]}]},onSubmit:function(){t("#"+this.toJSON().hex)}});r(n)}e.settings.color_picker_callback||(e.settings.color_picker_callback=t)});


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/scripts/framework/plugins/contextmenu/plugin.min.js:
--------------------------------------------------------------------------------
1 | tinymce.PluginManager.add("contextmenu",function(e){var t,n=e.settings.contextmenu_never_use_native;e.on("contextmenu",function(r){var i,o=e.getDoc();if(!r.ctrlKey||n){if(r.preventDefault(),tinymce.Env.mac&&tinymce.Env.webkit&&2==r.button&&o.caretRangeFromPoint&&e.selection.setRng(o.caretRangeFromPoint(r.x,r.y)),i=e.settings.contextmenu||"link image inserttable | cell row column deletetable",t)t.show();else{var a=[];tinymce.each(i.split(/[ ,]/),function(t){var n=e.menuItems[t];"|"==t&&(n={text:t}),n&&(n.shortcut="",a.push(n))});for(var s=0;s';
35 | 			});
36 | 
37 | 			emoticonsHtml += '';
38 | 		});
39 | 
40 | 		emoticonsHtml += '';
41 | 
42 | 		return emoticonsHtml;
43 | 	}
44 | 
45 | 	editor.addButton('emoticons', {
46 | 		type: 'panelbutton',
47 | 		panel: {
48 | 			role: 'application',
49 | 			autohide: true,
50 | 			html: getHtml,
51 | 			onclick: function(e) {
52 | 				var linkElm = editor.dom.getParent(e.target, 'a');
53 | 
54 | 				if (linkElm) {
55 | 					editor.insertContent(
56 | 						'' + linkElm.getAttribute('data-mce-alt') + ''
57 | 					);
58 | 
59 | 					this.hide();
60 | 				}
61 | 			}
62 | 		},
63 | 		tooltip: 'Emoticons'
64 | 	});
65 | });
66 | 


--------------------------------------------------------------------------------
/permission/src/main/resources/resources/scripts/framework/plugins/emoticons/plugin.min.js:
--------------------------------------------------------------------------------
1 | tinymce.PluginManager.add("emoticons",function(e,t){function n(){var e;return e='',tinymce.each(r,function(n){e+="",tinymce.each(n,function(n){var r=t+"/img/smiley-"+n+".gif";e+=''}),e+=""}),e+="
"}var r=[["cool","cry","embarassed","foot-in-mouth"],["frown","innocent","kiss","laughing"],["money-mouth","sealed","smile","surprised"],["tongue-out","undecided","wink","yell"]];e.addButton("emoticons",{type:"panelbutton",panel:{role:"application",autohide:!0,html:n,onclick:function(t){var n=e.dom.getParent(t.target,"a");n&&(e.insertContent(''+n.getAttribute('),this.hide())}},tooltip:"Emoticons"})}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/fullscreen/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("fullscreen",function(e){function t(){var e,t,n=window,r=document,i=r.body;return i.offsetWidth&&(e=i.offsetWidth,t=i.offsetHeight),n.innerWidth&&n.innerHeight&&(e=n.innerWidth,t=n.innerHeight),{w:e,h:t}}function n(){var e=tinymce.DOM.getViewPort();return{x:e.x,y:e.y}}function r(e){scrollTo(e.x,e.y)}function i(){function i(){f.setStyle(h,"height",t().h-(m.clientHeight-h.clientHeight))}var p,m,h,g,v=document.body,b=document.documentElement;d=!d,m=e.getContainer(),p=m.style,h=e.getContentAreaContainer().firstChild,g=h.style,d?(u=n(),o=g.width,a=g.height,g.width=g.height="100%",l=p.width,c=p.height,p.width=p.height="",f.addClass(v,"mce-fullscreen"),f.addClass(b,"mce-fullscreen"),f.addClass(m,"mce-fullscreen"),f.bind(window,"resize",i),i(),s=i):(g.width=o,g.height=a,l&&(p.width=l),c&&(p.height=c),f.removeClass(v,"mce-fullscreen"),f.removeClass(b,"mce-fullscreen"),f.removeClass(m,"mce-fullscreen"),f.unbind(window,"resize",s),r(u)),e.fire("FullscreenStateChanged",{state:d})}var o,a,s,l,c,u,d=!1,f=tinymce.DOM;return e.settings.inline?void 0:(e.on("init",function(){e.addShortcut("Ctrl+Shift+F","",i)}),e.on("remove",function(){s&&f.unbind(window,"resize",s)}),e.addCommand("mceFullScreen",i),e.addMenuItem("fullscreen",{text:"Fullscreen",shortcut:"Meta+Alt+F",selectable:!0,onClick:function(){i(),e.focus()},onPostRender:function(){var t=this;e.on("FullscreenStateChanged",function(e){t.active(e.state)})},context:"view"}),e.addButton("fullscreen",{tooltip:"Fullscreen",shortcut:"Meta+Alt+F",onClick:i,onPostRender:function(){var t=this;e.on("FullscreenStateChanged",function(e){t.active(e.state)})}}),{isFullscreen:function(){return d}})}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/hr/plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * plugin.js 3 | * 4 | * Released under LGPL License. 5 | * Copyright (c) 1999-2015 Ephox Corp. All rights reserved 6 | * 7 | * License: http://www.tinymce.com/license 8 | * Contributing: http://www.tinymce.com/contributing 9 | */ 10 | 11 | /*global tinymce:true */ 12 | 13 | tinymce.PluginManager.add('hr', function(editor) { 14 | editor.addCommand('InsertHorizontalRule', function() { 15 | editor.execCommand('mceInsertContent', false, '
'); 16 | }); 17 | 18 | editor.addButton('hr', { 19 | icon: 'hr', 20 | tooltip: 'Horizontal line', 21 | cmd: 'InsertHorizontalRule' 22 | }); 23 | 24 | editor.addMenuItem('hr', { 25 | icon: 'hr', 26 | text: 'Horizontal line', 27 | cmd: 'InsertHorizontalRule', 28 | context: 'insert' 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/hr/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("hr",function(e){e.addCommand("InsertHorizontalRule",function(){e.execCommand("mceInsertContent",!1,"
")}),e.addButton("hr",{icon:"hr",tooltip:"Horizontal line",cmd:"InsertHorizontalRule"}),e.addMenuItem("hr",{icon:"hr",text:"Horizontal line",cmd:"InsertHorizontalRule",context:"insert"})}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/media/moxieplayer.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/plugins/media/moxieplayer.swf -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/nonbreaking/plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * plugin.js 3 | * 4 | * Released under LGPL License. 5 | * Copyright (c) 1999-2015 Ephox Corp. All rights reserved 6 | * 7 | * License: http://www.tinymce.com/license 8 | * Contributing: http://www.tinymce.com/contributing 9 | */ 10 | 11 | /*global tinymce:true */ 12 | 13 | tinymce.PluginManager.add('nonbreaking', function(editor) { 14 | var setting = editor.getParam('nonbreaking_force_tab'); 15 | 16 | editor.addCommand('mceNonBreaking', function() { 17 | editor.insertContent( 18 | (editor.plugins.visualchars && editor.plugins.visualchars.state) ? 19 | ' ' : ' ' 20 | ); 21 | 22 | editor.dom.setAttrib(editor.dom.select('span.mce-nbsp'), 'data-mce-bogus', '1'); 23 | }); 24 | 25 | editor.addButton('nonbreaking', { 26 | title: 'Nonbreaking space', 27 | cmd: 'mceNonBreaking' 28 | }); 29 | 30 | editor.addMenuItem('nonbreaking', { 31 | text: 'Nonbreaking space', 32 | cmd: 'mceNonBreaking', 33 | context: 'insert' 34 | }); 35 | 36 | if (setting) { 37 | var spaces = +setting > 1 ? +setting : 3; // defaults to 3 spaces if setting is true (or 1) 38 | 39 | editor.on('keydown', function(e) { 40 | if (e.keyCode == 9) { 41 | 42 | if (e.shiftKey) { 43 | return; 44 | } 45 | 46 | e.preventDefault(); 47 | for (var i = 0; i < spaces; i++) { 48 | editor.execCommand('mceNonBreaking'); 49 | } 50 | } 51 | }); 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/nonbreaking/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("nonbreaking",function(e){var t=e.getParam("nonbreaking_force_tab");if(e.addCommand("mceNonBreaking",function(){e.insertContent(e.plugins.visualchars&&e.plugins.visualchars.state?' ':" "),e.dom.setAttrib(e.dom.select("span.mce-nbsp"),"data-mce-bogus","1")}),e.addButton("nonbreaking",{title:"Nonbreaking space",cmd:"mceNonBreaking"}),e.addMenuItem("nonbreaking",{text:"Nonbreaking space",cmd:"mceNonBreaking",context:"insert"}),t){var n=+t>1?+t:3;e.on("keydown",function(t){if(9==t.keyCode){if(t.shiftKey)return;t.preventDefault();for(var r=0;n>r;r++)e.execCommand("mceNonBreaking")}})}}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/noneditable/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("noneditable",function(e){function t(e){return function(t){return-1!==(" "+t.attr("class")+" ").indexOf(e)}}function n(t){function n(t){var n=arguments,r=n[n.length-2];return r>0&&'"'==a.charAt(r-1)?t:''+e.dom.encode("string"==typeof n[1]?n[1]:n[0])+""}var r=o.length,a=t.content,s=tinymce.trim(i);if("raw"!=t.format){for(;r--;)a=a.replace(o[r],n);t.content=a}}var r,i,o,a="contenteditable";r=" "+tinymce.trim(e.getParam("noneditable_editable_class","mceEditable"))+" ",i=" "+tinymce.trim(e.getParam("noneditable_noneditable_class","mceNonEditable"))+" ";var s=t(r),l=t(i);o=e.getParam("noneditable_regexp"),o&&!o.length&&(o=[o]),e.on("PreInit",function(){o&&e.on("BeforeSetContent",n),e.parser.addAttributeFilter("class",function(e){for(var t,n=e.length;n--;)t=e[n],s(t)?t.attr(a,"true"):l(t)&&t.attr(a,"false")}),e.serializer.addAttributeFilter(a,function(e){for(var t,n=e.length;n--;)t=e[n],(s(t)||l(t))&&(o&&t.attr("data-mce-content")?(t.name="#text",t.type=3,t.raw=!0,t.value=t.attr("data-mce-content")):t.attr(a,null))})})}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/pagebreak/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("pagebreak",function(e){var t="mce-pagebreak",n=e.getParam("pagebreak_separator",""),r=new RegExp(n.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,function(e){return"\\"+e}),"gi"),i='';e.addCommand("mcePageBreak",function(){e.settings.pagebreak_split_block?e.insertContent("

"+i+"

"):e.insertContent(i)}),e.addButton("pagebreak",{title:"Page break",cmd:"mcePageBreak"}),e.addMenuItem("pagebreak",{text:"Page break",icon:"pagebreak",cmd:"mcePageBreak",context:"insert"}),e.on("ResolveName",function(n){"IMG"==n.target.nodeName&&e.dom.hasClass(n.target,t)&&(n.name="pagebreak")}),e.on("click",function(n){n=n.target,"IMG"===n.nodeName&&e.dom.hasClass(n,t)&&e.selection.select(n)}),e.on("BeforeSetContent",function(e){e.content=e.content.replace(r,i)}),e.on("PreInit",function(){e.serializer.addNodeFilter("img",function(t){for(var r,i,o=t.length;o--;)if(r=t[o],i=r.attr("class"),i&&-1!==i.indexOf("mce-pagebreak")){var a=r.parent;if(e.schema.getBlockElements()[a.name]&&e.settings.pagebreak_split_block){a.type=3,a.value=n,a.raw=!0,r.remove();continue}r.type=3,r.value=n,r.raw=!0}})})}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/preview/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("preview",function(e){var t=e.settings,n=!tinymce.Env.ie;e.addCommand("mcePreview",function(){e.windowManager.open({title:"Preview",width:parseInt(e.getParam("plugin_preview_width","650"),10),height:parseInt(e.getParam("plugin_preview_height","500"),10),html:'",buttons:{text:"Close",onclick:function(){this.parent().parent().close()}},onPostRender:function(){var r,i="";i+='',tinymce.each(e.contentCSS,function(t){i+=''});var o=t.body_id||"tinymce";-1!=o.indexOf("=")&&(o=e.getParam("body_id","","hash"),o=o[e.id]||o);var a=t.body_class||"";-1!=a.indexOf("=")&&(a=e.getParam("body_class","","hash"),a=a[e.id]||"");var s=' ',l=e.settings.directionality?' dir="'+e.settings.directionality+'"':"";if(r=""+i+'"+e.getContent()+s+"",n)this.getEl("body").firstChild.src="data:text/html;charset=utf-8,"+encodeURIComponent(r);else{var c=this.getEl("body").firstChild.contentWindow.document;c.open(),c.write(r),c.close()}}})}),e.addButton("preview",{title:"Preview",cmd:"mcePreview"}),e.addMenuItem("preview",{text:"Preview",cmd:"mcePreview",context:"view"})}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/print/plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * plugin.js 3 | * 4 | * Released under LGPL License. 5 | * Copyright (c) 1999-2015 Ephox Corp. All rights reserved 6 | * 7 | * License: http://www.tinymce.com/license 8 | * Contributing: http://www.tinymce.com/contributing 9 | */ 10 | 11 | /*global tinymce:true */ 12 | 13 | tinymce.PluginManager.add('print', function(editor) { 14 | editor.addCommand('mcePrint', function() { 15 | editor.getWin().print(); 16 | }); 17 | 18 | editor.addButton('print', { 19 | title: 'Print', 20 | cmd: 'mcePrint' 21 | }); 22 | 23 | editor.addShortcut('Meta+P', '', 'mcePrint'); 24 | 25 | editor.addMenuItem('print', { 26 | text: 'Print', 27 | cmd: 'mcePrint', 28 | icon: 'print', 29 | shortcut: 'Meta+P', 30 | context: 'file' 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/print/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("print",function(e){e.addCommand("mcePrint",function(){e.getWin().print()}),e.addButton("print",{title:"Print",cmd:"mcePrint"}),e.addShortcut("Meta+P","","mcePrint"),e.addMenuItem("print",{text:"Print",cmd:"mcePrint",icon:"print",shortcut:"Meta+P",context:"file"})}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/save/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("save",function(e){function t(){var t;return t=tinymce.DOM.getParent(e.id,"form"),!e.getParam("save_enablewhendirty",!0)||e.isDirty()?(tinymce.triggerSave(),e.getParam("save_onsavecallback")?(e.execCallback("save_onsavecallback",e),void e.nodeChanged()):void(t?(e.setDirty(!1),t.onsubmit&&!t.onsubmit()||("function"==typeof t.submit?t.submit():n(e.translate("Error: Form submit field collision."))),e.nodeChanged()):n(e.translate("Error: No form element found.")))):void 0}function n(t){e.notificationManager.open({text:t,type:"error"})}function r(){var t=tinymce.trim(e.startContent);return e.getParam("save_oncancelcallback")?void e.execCallback("save_oncancelcallback",e):(e.setContent(t),e.undoManager.clear(),void e.nodeChanged())}function i(){var t=this;e.on("nodeChange dirty",function(){t.disabled(e.getParam("save_enablewhendirty",!0)&&!e.isDirty())})}e.addCommand("mceSave",t),e.addCommand("mceCancel",r),e.addButton("save",{icon:"save",text:"Save",cmd:"mceSave",disabled:!0,onPostRender:i}),e.addButton("cancel",{text:"Cancel",icon:!1,cmd:"mceCancel",disabled:!0,onPostRender:i}),e.addShortcut("Meta+S","","mceSave")}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/tabfocus/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("tabfocus",function(e){function t(e){9!==e.keyCode||e.ctrlKey||e.altKey||e.metaKey||e.preventDefault()}function n(t){function n(n){function o(e){return"BODY"===e.nodeName||"hidden"!=e.type&&"none"!=e.style.display&&"hidden"!=e.style.visibility&&o(e.parentNode)}function l(e){return/INPUT|TEXTAREA|BUTTON/.test(e.tagName)&&tinymce.get(t.id)&&-1!=e.tabIndex&&o(e)}if(s=r.select(":input:enabled,*[tabindex]:not(iframe)"),i(s,function(t,n){return t.id==e.id?(a=n,!1):void 0}),n>0){for(c=a+1;c=0;c--)if(l(s[c]))return s[c];return null}var a,s,l,c;if(!(9!==t.keyCode||t.ctrlKey||t.altKey||t.metaKey||t.isDefaultPrevented())&&(l=o(e.getParam("tab_focus",e.getParam("tabfocus_elements",":prev,:next"))),1==l.length&&(l[1]=l[0],l[0]=":prev"),s=t.shiftKey?":prev"==l[0]?n(-1):r.get(l[0]):":next"==l[1]?n(1):r.get(l[1]))){var u=tinymce.get(s.id||s.name);s.id&&u?u.focus():tinymce.util.Delay.setTimeout(function(){tinymce.Env.webkit||window.focus(),s.focus()},10),t.preventDefault()}}var r=tinymce.DOM,i=tinymce.each,o=tinymce.explode;e.on("init",function(){e.inline&&tinymce.DOM.setAttrib(e.getBody(),"tabIndex",null),e.on("keyup",t),tinymce.Env.gecko?e.on("keypress keydown",n):e.on("keydown",n)})}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/visualblocks/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("visualblocks",function(e,t){function n(){var t=this;t.active(o),e.on("VisualBlocks",function(){t.active(e.dom.hasClass(e.getBody(),"mce-visualblocks"))})}var r,i,o;window.NodeList&&(e.addCommand("mceVisualBlocks",function(){var n,a=e.dom;r||(r=a.uniqueId(),n=a.create("link",{id:r,rel:"stylesheet",href:t+"/css/visualblocks.css"}),e.getDoc().getElementsByTagName("head")[0].appendChild(n)),e.on("PreviewFormats AfterPreviewFormats",function(t){o&&a.toggleClass(e.getBody(),"mce-visualblocks","afterpreviewformats"==t.type)}),a.toggleClass(e.getBody(),"mce-visualblocks"),o=e.dom.hasClass(e.getBody(),"mce-visualblocks"),i&&i.active(a.hasClass(e.getBody(),"mce-visualblocks")),e.fire("VisualBlocks")}),e.addButton("visualblocks",{title:"Show blocks",cmd:"mceVisualBlocks",onPostRender:n}),e.addMenuItem("visualblocks",{text:"Show blocks",cmd:"mceVisualBlocks",onPostRender:n,selectable:!0,context:"view",prependToContext:!0}),e.on("init",function(){e.settings.visualblocks_default_state&&e.execCommand("mceVisualBlocks",!1,null,{skip_focus:!0})}),e.on("remove",function(){e.dom.removeClass(e.getBody(),"mce-visualblocks")}))}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/visualchars/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("visualchars",function(e){function t(t){function n(e){return''+e+""}function o(){var e,t="";for(e in p)t+=e;return new RegExp("["+t+"]","g")}function a(){var e,t="";for(e in p)t&&(t+=","),t+="span.mce-"+p[e];return t}var s,l,c,u,d,f,p,m,h=e.getBody(),g=e.selection;if(p={"\xa0":"nbsp","\xad":"shy"},r=!r,i.state=r,e.fire("VisualChars",{state:r}),m=o(),t&&(f=g.getBookmark()),r)for(l=[],tinymce.walk(h,function(e){3==e.nodeType&&e.nodeValue&&m.test(e.nodeValue)&&l.push(e)},"childNodes"),c=0;c=0;c--)e.dom.remove(l[c],1);g.moveToBookmark(f)}function n(){var t=this;e.on("VisualChars",function(e){t.active(e.state)})}var r,i=this;e.addCommand("mceVisualChars",t),e.addButton("visualchars",{title:"Show invisible characters",cmd:"mceVisualChars",onPostRender:n}),e.addMenuItem("visualchars",{text:"Show invisible characters",cmd:"mceVisualChars",onPostRender:n,selectable:!0,context:"view",prependToContext:!0}),e.on("beforegetcontent",function(e){r&&"raw"!=e.format&&!e.draft&&(r=!0,t(!1))})}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/plugins/wordcount/plugin.min.js: -------------------------------------------------------------------------------- 1 | tinymce.PluginManager.add("wordcount",function(e){function t(){e.theme.panel.find("#wordcount").text(["Words: {0}",i.getCount()])}var n,r,i=this;n=e.getParam("wordcount_countregex",/[\w\u2019\x27\-\u00C0-\u1FFF]+/g),r=e.getParam("wordcount_cleanregex",/[0-9.(),;:!?%#$?\x27\x22_+=\\\/\-]*/g),e.on("init",function(){var n=e.theme.panel&&e.theme.panel.find("#statusbar")[0];n&&tinymce.util.Delay.setEditorTimeout(e,function(){n.insert({type:"label",name:"wordcount",text:["Words: {0}",i.getCount()],classes:"wordcount",disabled:e.settings.readonly},0),e.on("setcontent beforeaddundo",t),e.on("keyup",function(e){32==e.keyCode&&t()})},0)}),i.getCount=function(){var t=e.getContent({format:"raw"}),i=0;if(t){t=t.replace(/\.\.\./g," "),t=t.replace(/<.[^<>]*?>/g," ").replace(/ | /gi," "),t=t.replace(/(\w+)(&#?[a-z0-9]+;)+(\w+)/i,"$1$3").replace(/&.+?;/g," "),t=t.replace(r,"");var o=t.match(n);o&&(i=o.length)}return i}}); -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/simplify_dashboard.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | //Flot Chart (Total Sales) 4 | var d1 = []; 5 | for (var i = 0; i <= 10; i += 1) { 6 | //d1.push([i, parseInt(Math.random() * 30)]); 7 | d1 = [[0,700],[1,1200],[2,1100],[3,900],[4,500],[5,700],[6,500],[7,600],[8,1200],[9,1700],[10,1200]]; 8 | } 9 | 10 | function plotWithOptions() { 11 | $.plot("#placeholder", [d1], { 12 | series: { 13 | lines: { 14 | show: true, 15 | fill: true, 16 | fillColor: '#eee', 17 | steps: false, 18 | 19 | }, 20 | points: { 21 | show: true, 22 | fill: false 23 | } 24 | }, 25 | 26 | grid: { 27 | color: '#fff', 28 | hoverable: true, 29 | autoHighlight: true, 30 | }, 31 | colors: [ '#bbb'], 32 | }); 33 | } 34 | 35 | $("
").css({ 36 | position: "absolute", 37 | display: "none", 38 | border: "1px solid #222", 39 | padding: "4px", 40 | color: "#fff", 41 | "border-radius": "4px", 42 | "background-color": "rgb(0,0,0)", 43 | opacity: 0.90 44 | }).appendTo("body"); 45 | 46 | $("#placeholder").bind("plothover", function (event, pos, item) { 47 | 48 | var str = "(" + pos.x.toFixed(2) + ", " + pos.y.toFixed(2) + ")"; 49 | $("#hoverdata").text(str); 50 | 51 | if (item) { 52 | var x = item.datapoint[0], 53 | y = item.datapoint[1]; 54 | 55 | $("#tooltip").html("Total Sales : " + y) 56 | .css({top: item.pageY+5, left: item.pageX+5}) 57 | .fadeIn(200); 58 | } else { 59 | $("#tooltip").hide(); 60 | } 61 | }); 62 | 63 | }); 64 | -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce-small.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce-small.eot -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce-small.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce-small.ttf -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce-small.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce-small.woff -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce.eot -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce.ttf -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/skins/lightgray/fonts/tinymce.woff -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/skins/lightgray/img/anchor.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/skins/lightgray/img/anchor.gif -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/skins/lightgray/img/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/skins/lightgray/img/loader.gif -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/skins/lightgray/img/object.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/skins/lightgray/img/object.gif -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/skins/lightgray/img/trans.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momokanni/security-wrapper/f23a1682ed789765d9e30b6325ba30c6a3e1edda/permission/src/main/resources/resources/scripts/framework/skins/lightgray/img/trans.gif -------------------------------------------------------------------------------- /permission/src/main/resources/resources/scripts/framework/uploader.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * angular-ui-uploader 3 | * https://github.com/angular-ui/ui-uploader 4 | * Version: 1.1.3 - 2015-12-01T00:54:49.732Z 5 | * License: MIT 6 | */ 7 | !function(){"use strict";function o(o){function e(o){for(var e=0;e hr { 41 | margin: 30px 0; 42 | } 43 | 44 | /* Main marketing message and sign up button */ 45 | .jumbotron { 46 | text-align: center; 47 | border-bottom: 1px solid #e5e5e5; 48 | } 49 | .jumbotron .btn { 50 | font-size: 21px; 51 | padding: 14px 24px; 52 | } 53 | 54 | /* Supporting marketing content */ 55 | .marketing { 56 | margin: 40px 0; 57 | } 58 | .marketing p + h4 { 59 | margin-top: 28px; 60 | } 61 | 62 | /* Responsive: Portrait tablets and up */ 63 | @media screen and (min-width: 768px) { 64 | .container { 65 | max-width: 730px; 66 | } 67 | 68 | /* Remove the padding we set earlier */ 69 | .header, 70 | .marketing, 71 | .footer { 72 | padding-left: 0; 73 | padding-right: 0; 74 | } 75 | /* Space out the masthead */ 76 | .header { 77 | margin-bottom: 30px; 78 | } 79 | /* Remove the bottom border on the jumbotron for visual effect */ 80 | .jumbotron { 81 | border-bottom: 0; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /permission/src/main/resources/resources/views/commons/confirm.html: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | -------------------------------------------------------------------------------- /permission/src/main/resources/resources/views/commons/partials/content.html: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 |
7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 |
-------------------------------------------------------------------------------- /permission/src/main/resources/resources/views/commons/partials/offsidebar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | 16 |
17 |
18 |
19 |
20 |
22 |
23 |
24 |
25 | 26 | 27 |
28 |
29 |
-------------------------------------------------------------------------------- /permission/src/main/resources/resources/views/platform/roleForm.html: -------------------------------------------------------------------------------- 1 |
2 |
角色信息
3 |
4 |
5 |
6 |
7 | 8 |
9 | 10 | 11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------