common-security-core common-security-validate common-security-browser common-security-app
security.properties
文件中。      
    
    @ComponentScan(basePackages = {"com.chentongwei.security", "你自己的包名"})
  
我们的系统都会带有登录功能,而登录功能常用的无非就三种:表单登录、短信登录、第三方登录。
表单登录的话大多都会带有验证码功能,而验证码(主流的有图形验证码也有极验证)我们需要写接口生成,也需要写一些判断来验证是否正确,短信登录的话我们也需要验证短信验证码是否匹配等操作。还有URL安全问题,比如我不知道你密码,但是我却知道你的访问url,那么我直接敲url,应该会让我们跳转到登录页去等。
    common-security-validate
能完美解决以上全部问题:  
1、直接调用        code/image
接口即可获取图形验证码,返回的是base64编码后的。Demo如下:      
<img id="imageCode" src="" onclick="getImageCode()" />
window.onload = function () {
    getImageCode();
}
function getImageCode() {
    $.ajax({
        url: "code/image",
        type: "get",
        /**
         * 为什么要在headers里传个deviceId参数?
         * 若你是前后端不分离(采取session共享数据),则无需传递此header里的参数。
         * 若你是前后端分离(采取redis共享数据),则需要在header里传递deviceId参数,
         * 若你是APP,则此参数建议取设备id,若你是web前后分离,则可以从后台获取一个然后放到缓存中,并放到此处。它标记着一个唯一的用户。
        */
        headers: {
            "deviceId": "123"
        },
        success: function (data) {
            $("#imageCode").attr("src", "data:image/jpeg;base64," + data);
        }
    });
}
    2、上面介绍了图形验证码,这里看下极验证:
 2.1、首先去极验官网注册账号并创建项目,然后获取到id和key。
	2.2、然后在配置文件里配置好,并直接调用        code/geetest
接口即可获取极验验证码。Demo如下:      
        security.properties
      
com.chentongwei.security.code.geetest.id=f4fac365b3ccb82ed68cc8c1ab6346f6 com.chentongwei.security.code.geetest.key=c91ca7c31cf0d8a40c5cf5714624bfb2 com.chentongwei.security.code.geetest.newfailback=true
        html+js
      
<div class="row cl">
    <div id="captcha" class="formControls col-xs-8 col-xs-offset-3">
        <p id="wait">正在加载验证码......</p>
    </div>
</div>
<button id="popup-submit">测试登录</button>
$.ajax({
    // 获取id,challenge,success(是否启用failback)
    url: "code/geetest?t=" + (new Date()).getTime(), // 加随机数防止缓存
    type: "get",
    dataType: "json",
    /**
     * 为什么要在headers里传个deviceId参数?
     * 若你是前后端不分离(采取session共享数据),则无需传递此header里的参数。
     * 若你是前后端分离(采取redis共享数据),则需要在header里传递deviceId参数,
     * 若你是APP,则此参数建议取设备id,若你是web前后分离,则可以从后台获取一个然后放到缓存中,并放到此处。它标记着一个唯一的用户。
    */
    headers: {
        "deviceId": "123"
    },
    success: function (data) {
        console.log("data:" , data);
        // 使用initGeetest接口
        // 参数1:配置参数
        // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件
        initGeetest({
            gt: data.gt,
            challenge: data.challenge,
            product: "popup", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效
            offline: !data.success // 表示用户后台检测极验服务器是否宕机,与SDK配合,用户一般不需要关注
        }, handlerPopup);
    }
});
// 代码详细说明
var handlerPopup = function (captchaObj) {
    console.info(captchaObj)
    // 注册提交按钮事件,比如在登陆页面的登陆按钮
    $("#popup-submit").click(function () {
        // 此处省略在登陆界面中,获取登陆数据的一些步骤
        // 先校验是否点击了验证码
        var validate = captchaObj.getValidate();
        if (!validate) {
            alert('请先完成验证!');
            return;
        }
        // 提交验证码信息,比如登陆页面,你需要提交登陆信息,用户名和密码等登陆数据
        $.ajax({
            url: "login",
            type: "post",
            // dataType: "json",
            data: {
                // 用户名和密码等其他数据,自己获取,不做演示
                username:"13123",
                password:"123456",
                // 验证码数据,这些数据不用自己获取
                // 这是二次验证所需的三个值
                // 当然,你也可以直接设置验证码单独校验,省略其他信息
                geetest_challenge: validate.geetest_challenge,
                geetest_validate: validate.geetest_validate,
                geetest_seccode: validate.geetest_seccode
            },
            headers: {
                "deviceId": "123"
            },
            // 这里是正确返回处理结果的处理函数
            // 假设你就返回了1,2,3
            // 当然,正常情况是返回JSON数据
            success: function (result) {
                alert("登陆成功!" + result);
            }
        });
    });
    // 将验证码加到id为captcha的元素里
    // 验证码将会在下面指定的元素中显示出来
    captchaObj.appendTo("#captcha");
    captchaObj.onReady(function () {
        $("#wait").hide();
    });
};
    3、校验全都封装好了,你只需要在配置文件里配置上需要验证码的url即可。Demo如下:
# 需要图形验证码拦截的url com.chentongwei.security.code.image.url=/login # 需要极验验证码拦截的url com.chentongwei.security.code.geetest.url=/login, /hello
PS:在我们对如上配置的url进行访问的时候,会进行验证码的校验操作。
比如:我们在/login登录接口上做了验证码配置,则直接传递用户名密码请求login的话是会被拦截报错的,会提示验证码不能为空等字样。
具体拦截如下内容:
验证码的值不能为空 验证码不存在,请刷新页面重试 验证码已过期 验证码不匹配
4、直接配置文件里面配置上哪些url无需权限即可,若不配置,则默认拦截全部url(意味着只有登录才可以访问)。
# 无需登录即可访问的url com.chentongwei.security.authorize.permitUrls=/hello, /hello2
# 图形验证码长度 com.chentongwei.security.code.image.length=6 # 图形验证码宽度 com.chentongwei.security.code.image.width=150 # 图形验证码高度 com.chentongwei.security.code.image.height=80 # 图形验证码过期时间(s) com.chentongwei.security.code.image.expireIn=120 # 需要图形验证码拦截的url com.chentongwei.security.code.image.url=/login
com.chentongwei.security.code.geetest.id=f4fac365b3ccb82ed68cc8c1ab6346f6 com.chentongwei.security.code.geetest.key=c91ca7c31cf0d8a40c5cf5714624bfb2 com.chentongwei.security.code.geetest.newfailback=true # 极验验证码过期时间(s) com.chentongwei.security.code.geetest.expireIn=120 # 需要极验验证码拦截的url com.chentongwei.security.code.geetest.url=/login, /hello
# 短信验证码长度 com.chentongwei.security.code.sms.length=6 # 短信验证码过期时间 com.chentongwei.security.code.sms.expireIn=120 # 需要短信验证码拦截的url com.chentongwei.security.code.sms.url=/mobile
# redis为前后分离,验证码存储采用redis,默认不写是session的方式,前后不分离。 # 若采取的是redis方式,千万记得在验证码接口参数中像header里传递deviceId,这个在前面demo中有注释详细说明 com.chentongwei.security.code.repository=redis
# 需要设置同ip在一定时间内最多可访问同url几次的url路径 com.chentongwei.security.authorize.ipValidateUrl=/hello, /hello2 # 同ip在一定时间内最多可访问同url几次的时间配置(秒) com.chentongwei.security.authorize.ipValidateSeconds=5 # 同ip在一定时间内最多可访问同url几次的次数配置 com.chentongwei.security.authorize.ipValidateCount=5
# 无需登录即可访问的url com.chentongwei.security.authorize.permitUrls=/hello,/hello2 # 若出现401状态码,则跳转到固定page com.chentongwei.security.authorize.unAuthorizePage=/default-login.html
PS:
1、默认拦截全部url
2、访问没权限的url,返回401状态码,若配置了unAuthorizePage,则跳转到unAuthorizePage页面,若没配置unAuthorizePage,则返回如下JSON:
{"code":401,"msg":"Unauthorized","data":null}
  # REDIRECT/JSON 认证成功/失败后重定向到页面还是返回JSON com.chentongwei.security.authentication.loginType=REDIRECT # REDIRECT的方式,认证失败后跳转到的页面url(默认URL是及其不友好的),默认,此配置JSON方式无效 com.chentongwei.security.authentication.loginErrorPage=http://www.tucaole.cn # REDIRECT的方式,认证成功后跳转到的页面url(默认URL是上一次访问的URL),此配置JSON方式无效 com.chentongwei.security.authentication.loginSuccessPage=http://www.tucaole.cn
PS:若你不喜欢我生成的图形验证码,比如嫌弃我的条纹太少了,样式太丑了神马的,完全可以自定义一个验证码接口,放心,即使是你自定义的,我的验证一样会生效。
自定义接口前提:
1、bean的id为      imageValidateCodeGenerator
    
2、实现      com.chentongwei.security.validate.code.ValidateCodeGenerator
    
@Component("imageValidateCodeGenerator")
public class ImageValidateCodeGenerator implements ValidateCodeGenerator  {
    
    private final Logger logger = LoggerFactory.getLogger(getClass());
    @Override
    public ValidateCode generate(ServletWebRequest request) {
        logger.info("自定义的验证码生成逻辑....");
        return new ValidateCode();
    }
}
  PS:若你不喜欢我生成的极验验证码完全可以自定义一个验证码接口,放心,即使是你自定义的,我的验证一样会生效。
自定义接口前提:
1、bean的id为      geetestValidateCodeGenerator
    
2、实现      com.chentongwei.security.validate.code.ValidateCodeGenerator
    
@Component("geetestValidateCodeGenerator")
public class GeetestValidateCodeGenerator implements ValidateCodeGenerator {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    @Override
    public ValidateCode generate(ServletWebRequest request) {
        logger.info("自定义的验证码生成逻辑....");
        return new ValidateCode();
    }
}
  PS:我默认的短信验证码生成完全没用,我就在控制台输出了一串数字,因为这个短信平台的厂商不同,实现方式不同,所以我提供一个接口,你们自行实现自己对接的厂商即可。放心,即使是你自定义的,我的验证一样会生效。
自定义接口前提:
1、实现      com.chentongwei.security.validate.code.sms.SmsCodeSender
    
public class MySmsCodeSender implements SmsCodeSender {
    @Override
    public void send(String mobile, String code) {
        System.out.println("send.......");
    }
}
  PS:
自定义成功处理器,前提只有一个,bean的id为      authenticationSuccessHandler
    
@Component("authenticationSuccessHandler")
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
	
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        logger.info("验证成功!");
    }
}
  PS:
自定义失败处理器,前提只有一个,bean的id为      authenticationFailureHandler
    
@Component("authenticationFailureHandler")
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    private Logger logger = LoggerFactory.getLogger(getClass());
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        logger.info("验证失败!");
    }
}
  PS:用过SpringSecurity的人一定知道UserDetailsService,我内置了一个DefaultUserDetailsService,没任何用处,唯一用途就是启动的时候若没有写UserDetailsService的实现,不会出错,但是大家一定要写UserDetailsService的实现,来完成自己的登录业务逻辑,我的只是为了启动不报错而生的!!
PS:我内置了一个叫      ValidateSecurityCoreConfig
的核心配置,里面只有一些基础的配置,比如:成功失败处理器,接口权限等。没有包含session、logout等等一堆东西,若我的满足不了你们的需求,则大可自定义。    
自定义配置前提:
1、需要在配置文件里指定enable=false,缺省值是true
# 自定义核心配置,false:自定义;true:采用我的默认配置,缺省值为true com.chentongwei.security.core.config.enable=false
这样就可以自定义SpringSecurity的配置了。
2、自定义的SpringSecurity配置类:
建议至少包含我默认的      ValidateSecurityCoreConfig
类里的注入的bean,而你只增加注入的bean和修改configure方法,因为我注入的bean有成功失败处理器,验证码校验器,短信配置等,这些功能你都不用的话,那就别用此框架啦!!!    
还有一个非常关键的是:
@Autowired private AuthorizeConfigManager authorizeConfigManager; // 一定要放到最后,是因为config方法里最后做了其他任何方法都需要身份认证才能访问。 // 放到前面的话,后面在加载.antMatchers(getPermitUrls()).permitAll()的时候也会被认为无权限, // 因为前面已经做了其他任何方法都需要身份认证才能访问,SpringSecurity是有先后顺序的。 authorizeConfigManager.config(http);
若我不想自定义配置类,我想在你这配置类里追加几个      HttpSecurity
类里的方法能行吗?    
能行,在你项目中写个类实现      com.chentongwei.security.core.authorize.AuthorizeConfigProvider
即可,并且      指定@Order(Integer.MAX_VALUE - 100)
,其他什么都不用管。会自动生效!!!    
比如
@Component
@Order(Integer.MAX_VALUE - 100)
public class DemoAuthorizeConfigProvider implements AuthorizeConfigProvider {
    @Override
    public void config(HttpSecurity httpConfig) {
        httpSecurity.authorizeRequests().antMatchers("/hello2").hasRole("admin");
    }
}
    
还有一个需要注意的地方,就是再比如:如果我们既要自定义      httpSecurity.authorizeRequests().antMatchers
又要自定义      config.anyRequest()
,这时候不能写到一个类(比如上面的      DemoAuthorizeConfigProvider
)里面,这时候要写两个类,一个是上面的那个类,还需要单独写一个类里面单独去定义      httpSecurity.authorizeRequests().anyRequest()
,并且指定      @Order(Integer.MAX_VALUE)
,比如:    
@Component
@Order(Integer.MAX_VALUE)
public class DemoAnyRequestAuthorizeConfigProvider implements AuthorizeConfigProvider {
    @Override
    public void config(HttpSecurity httpSecurity) {
        httpSecurity.authorizeRequests().anyRequest().access("@rbacService.hasPermission(request,authentication)");
    }
}
    总结注意如下四点:
1、实现AuthorizeConfigProvider接口。
2、若要配置非      httpSecurity.authorizeRequests().anyRequest
(比如:      httpSecurity.authorizeRequests().antMatchers
)的时候,需要指定      @Order(Integer.MAX_VALUE - 100)
;而配置      httpSecurity.authorizeRequests().anyRequest
的时候,需要指定      @Order(Integer.MAX_VALUE)
。    
3、自定义      httpSecurity.authorizeRequests().anyRequest
和非      httpSecurity.authorizeRequests().anyRequest
需要写两个不同的类,并指定不同的@Order。    
4、除了内置的几个接口(如:验证码、登录等)不需要认证身份即可访问,其他的接口都是需要身份认证才可访问(      httpSecurity.authorizeRequests().anyRequest().authenticated();
)。    
common-security-validate
,并在它基础上增加了登出操作、记住我、session管理以及是否允许iframe操作;适用于前后端不分离(session存储数据)。  security.properties
文件中。      
    
    @ComponentScan(basePackages = {"com.chentongwei.security", "你自己的包名"})
  
PS:上面的    common-security-validate
里的所有配置全都可以使用。并在原基础上增加了如下三部分。  
# 退出登录接口,缺省值为/logout com.chentongwei.security.logout.logoutUrl=/logout2 # 缺省值:/default-login.html,是跳转到URL还是返回JSON,只要这里配置了URL(并且不为缺省值),那就是跳转到URL com.chentongwei.security.logout.logoutSuccessUrl=/login.html
PS:默认登出接口为/logout,这个可自定义,而且不管定义成什么,直接配置文件里写一下就行了,无需真的写一个logout控制器接口,内部原理会先将记住我的表里的当前用户删除掉,并清除JSESSIONID。
登出后默认是返回如下JSON数据,若配置了          logoutSuccessUrl
的话,则登出成功后跳转到          logoutSuccessUrl
对应的value值。        
{"code":200,"msg":"退出成功"}
        示例:
<!-- 注意此处的/logout2就是上面配置文件所配置的logoutUrl,默认为/logout --> <a href="/logout2">退出</a>
PS:前提:需要建立一张用于记住我的表,这个表是SpringSecurity的记住我功能必须要建立的。建表语句如下:
CREATE TABLE persistent_logins (
    username VARCHAR (64) NOT NULL,
    series VARCHAR (64) PRIMARY KEY,
    token VARCHAR (64) NOT NULL,
    last_used TIMESTAMP NOT NULL
)
      记住我配置项:
# 记住我时长(缺省值3600s) # 注意:需要提前建表 com.chentongwei.security.rememberme.seconds=7200
PS:记住我功能简述:
一般前后不分离的项目我们都会将用户信息存到session,意味着服务器重启的时候就需要每个用户都重新登录才可以,而记住我就是做了持久化,记住我后,会将          username
,          token
等信息放到刚才那张表中,然后首次登陆会根据          token
去表里查询是否存在对应的信息,以及是否超时等,若符合要求则直接登陆。第一次验证成功后会存到          cookie
中,所以并不是每次都查询表,这样一来也不会产生性能问题。        
若你用了记住我功能,则复选框的          name
必须是          remember-me
。        
示例:
          <input name="remember-me" type="checkbox" value="true" />记住我
        
# session失效/被踢掉时跳转的地址,默认不配置,不配置则代表返回JSON格式 com.chentongwei.security.session.sessionInvalidUrl=/login.html # 同一个用户在系统中最大的session数,默认1 com.chentongwei.security.session.maximumSessions=1
PS:
1、默认session失效后返回如下JSON数据,若配置了          sessionInvalidUrl
则session失效后会跳转到对应的value。        
{"code":601,"msg":"session已失效"}
        
2、默认一个用户在系统中最大的登录数为1,当有多个登录数时,会把上一个登录的人给踢掉,踢掉后默认返回如下JSON数据,配置了          sessionInvalidUrl
则session失效后会跳转到对应的value。若想一个账号在系统中最大登录数为多个,则只需修改          maximumSessions
即可。        
{"code":601,"msg":"session已失效可能是您的账号在别处登录导致的"}
      # 1:放开frame的拦截权限。缺省值;0:不允许frame嵌套 com.chentongwei.security.frame.disableStatus=1
PS:默认不允许iframe嵌套,防止csrf攻击。若有需要,则可以配置        disableStatus=1
即可。      
common-security-validate
,并在它基础上增加了JWT、登出操作、用户并发登录管理等操作;适用于前后端分离(redis存储数据)。  security.properties
文件中。      
    
    @ComponentScan(basePackages = {"com.chentongwei.security", "你自己的包名"})
  
PS:上面的      common-security-validate
里的所有配置全都可以使用。并在原基础上增加了如下三部分。    
用户登录后会在Header里返回JWT生成的Token。此后任何需要认证才可访问的接口都需要携带此Token在Header中。
# JWT签名密钥,默认为defaultSecret com.chentongwei.security.jwt.secret=tucaole # JWT过期时间(秒),默认为3600s,一小时 com.chentongwei.security.jwt.expiration=120 # md5Key,每个token都对应一个唯一的md5key,默认值为randomKey com.chentongwei.security.jwt.md5Key=tucaolemd5 # GET请求是否需要进行Authentication请求头校验,true:默认校验;false:不拦截GET请求 com.chentongwei.security.jwt.preventsGetMethod=false
PS:只是SpringSecurity整合的JWT,并没有融入Oauth2。
# 判断token还剩余多少秒到期后自动刷新Token,并放到header里。默认是60s,-1为不刷新 com.chentongwei.security.jwt.autoRefreshTokenExpiration=60
# 判断是否开启允许多人同账号同时在线,若不允许的话则将上一个人T掉,默认false,不T掉,允许多人登录,true:T掉 com.chentongwei.security.jwt.preventsLogin=true
# 内置了退出接口:/jwtLogout
# 是否要覆盖认证成功处理器,若要则指定enable=false。并且bean的name为“authenticationSuccessHandler” com.chentongwei.security.app.success.handler.enable=false # 是否要覆盖认证失败处理器,若要则指定enable=false。并且bean的name为“authenticationFailureHandler” com.chentongwei.security.app.failure.handler.enable=false