CAS 实现站内单点登录及实现第三方 OAuth、OpenId 登录(二)

一、登录表单

1<form:form id="loginForm" method="post" commandName="${commandName}" htmlEscape="true"> 2    <form:errors path="*" element="em" cssClass="" /> 3    <c:if test="${not empty sessionScope.openIdLocalId}"> 4        用户名<input id="username" name="username" type="text" value="${sessionScope.openIdLocalId}" /> 5    </c:if> 6    <c:if test="${empty sessionScope.openIdLocalId}"> 7        <!-- 这里您不要直接使用 input,而使用 springframework 的 form:input 能够在登录出错时,还能保证显示之前登录时输入的用户名 --> 8        <form:input id="username" path="username" htmlEscape="true" /> 9    </c:if> 10    密码:<input id="password" name="password" type="password" /> 11    验证码:<input id="validateCode" name="validateCode" type="text" /><img src="https://my.oschina.net/captcha.jpg" /> 12    <input id="rememberMe" name="rememberMe" type="checkbox" value="true" /><label for="rememberMe">记住用户名</label> 13    <input id="login-Btn" type="button" value="登录" /> 14    <input name="lt" type="hidden" value="${loginTicket}" /> 15    <input name="execution" type="hidden" value="${flowExecutionKey}" /> 16    <input name="_eventId" type="hidden" value="submit" /> 17</form:form> 18<!-- 需要 jquery.cookie 和 jquery.md5 插件 --> 19<script type="text/javascript"> 20    $(document).ready(function(){ 21        var loginUsername = $.cookie("loginUsername"); 22        if(loginUsername){ 23            $("#username").val(loginUsername); 24            $("#rememberMe").attr("checked", true); 25        } 26        $("#login-Btn").click(function(){ 27            /* 为了密码传输安全,表单提交之前,进行 md5 加密 */ 28            $("#password").val($.md5($("#password").val())); 29            if($("#rememberMe").is(":checked") == true){ 30                $.cookie("loginUsername", $("#username").val(), {expires: 365}); 31            }else{ 32                $.cookie("loginUsername", null, {expires: -1}); 33            } 34            $("#loginForm").submit(); 35        }); 36    }); 37</script>

二、验证码

        此例子中的验证实现是使用的 Google 的 Kaptcha

  1. Google Kaptcha 配置(在 WEB-INF/spring-configuration/applicationContext.xml 添加)

    1<bean id="captchaConfig" class="com.google.code.kaptcha.util.Config"> 2    ... ... 3</bean> 4<bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha" p:config-ref="captchaConfig" />
  2. Servlet 配置

    1)修改 WEB-INF/cas-servlet.xml 中的

    1<bean id="authenticationViaFormAction" class="org.jasig.cas.web.flow.AuthenticationViaFormAction" 2        p:centralAuthenticationService-ref="centralAuthenticationService" 3        p:warnCookieGenerator-ref="warnCookieGenerator"/>

        为 : AuthenticationCaptchaViaFormAction.java(用户验证 Action)

    1<bean id="authenticationViaFormAction" class="com.buession.cas.web.flow.AuthenticationCaptchaViaFormAction" 2        p:captchaConfig-ref="captchaConfig" 3        p:centralAuthenticationService-ref="centralAuthenticationService" 4        p:warnCookieGenerator-ref="warnCookieGenerator" />

    2)在 WEB-INF/cas-servlet.xml 添加
    CaptchaController.java(验证码控制器)
    ValidateCaptchaController.java(验证码异步验证控制器)

    1<bean id="handlerMappingC" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> 2    <property name="mappings"> 3        <props> 4            <prop key="/captcha.jpg">captchaController</prop><!-- 验证码 Controller --> 5            <prop key="/validateCaptcha">validateCaptchaController</prop><!-- 验证码异步验证 Controller,不是必须 --> 6            ... ... 7        </props> 8    </property> 9</bean> 10<bean id="captchaController" class="com.buession.cas.web.controller.CaptchaController" 11    p:config-ref="captchaConfig" /> 12<bean id="validateCaptchaController" class="com.buession.cas.web.controller.ValidateCaptchaController"  13    p:config-ref="captchaConfig" />

    3)记得在 web.xml 添加 Servlet 和 URL 之间映射

三、用户认证

  1. 数据库配置(在添加 WEB-INF/spring-configuration/applicationContext.xml 添加)

    1<bean id="masterJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" 2    p:dataSource-ref="masterDataSource" /> 3<bean id="slaveJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" 4    p:dataSource-ref="slaveDataSource" /> 5 6<bean id="masterDataSource" class="org.apache.commons.dbcp.BasicDataSource" 7    ... ... 8    destroy-method="close" /> 9<bean id="slaveDataSource" class="org.apache.commons.dbcp.BasicDataSource" 10    ... ... 11    destroy-method="close" />

    为什么要配置 master 数据源和 slave 数据源?
    用户登录时,查询用户信息是只读,我们就从从库中查询,主库是可以用于向数据库中写入用户登录日志,或者密码错误次数(当然这里可以根据实际需求调整,比如用户密码输入错误次数,也是可以记录到 Memcache、Redis 等缓存服务器中的)

  2. 修改用户凭证 credentials(WEB-INF/login-webflow.xml)

    修改

    <var name="credentials" class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" />
    

    为:RememberMeUsernamePasswordCaptchaCredentials.java(带有 ”RememberMe“ 和 ”验证码“的用户凭证)

    <var name="credentials" class="com.buession.cas.authentication.principal.RememberMeUsernamePasswordCaptchaCredentials" />
    
  3. 修改 AuthenticationViaFormAction(WEB-INF/login-webflow.xml)

    修改

    1<view-state id="viewLoginForm" view="casLoginView" model="credentials"> 2    <binder> 3        <binding property="username" /> 4        <binding property="password" /> 5    </binder> 6    ... ... 7</view-state>

    为(binding 的 property 属性值一定要与 Credentials 的属性名和登录表单控件的 name 属性的值保持一致)

    1<view-state id="viewLoginForm" view="casLoginView" model="credentials"> 2    <binder> 3        <binding property="username" /> 4        <binding property="password" /> 5        <binding property="validateCode" /> 6    </binder> 7    ... ... 8</view-state>
  4. 添加数据库 authentication handlers(在 WEB-INF/deployerConfigContext.xml 中添加)

    1<bean id="authenticationManager" class="org.jasig.cas.authentication.AuthenticationManagerImpl"> 2    ... ... 3    <property name="authenticationHandlers"> 4        <list> 5            <bean class="com.domain.authentication.handler.DatabaseAuthenticationHandler"> 6                <property name="jdbcTemplate" ref="slaveJdbcTemplate" /> 7                <!--(1)--><property name="sql" value="SELECT `password`, `salt`, `algo` FROM `member` WHERE `username` = ? LIMIT 1" /> 8                <!--(2)--><property name="sql" value="SELECT `password`, `salt`, `algo` FROM `member` WHERE `username` = ? OR `email` = ? OR `mobile` = ? LIMIT 1" /> 9                <!--(3)--><property name="sql" value="SELECT `password`, `salt`, `algo` FROM `member` WHERE `username` = ? OR `email` = ? OR `mobile` = ?" /> 10                <!-- (1)适用于单一条件登录;(2)适用于多条件,且这些条件中不会出现相同值的情况;(3)适用于多条件,但这些条件中可能会存在相同值的情况,例如:存在 username 为:13800138000,和 mobile 也为:13800138000 的不同两条用户数据 --> 11                <property name="passwordEncoder" ref="passwordEncoder" /> 12            </bean> 13        </list> 14    </property> 15</bean> 16 17class DatabaseAuthenticationHandler extends com.buession.cas.authentication.handler.support.DatabaseQueryAuthenticationHandler { 18 19    @Override 20    protected boolean authenticateUsernamePasswordInternal(UsernamePasswordCredentials credentials) 21            throws AuthenticationException { 22        PasswordEncoder passwordEncoder = (PasswordEncoder) getPasswordEncoder(); 23        String username = getPrincipalNameTransformer().transform(credentials.getUsername()); 24        String password = credentials.getPassword(); 25 26        (1)(2) 27        try { 28            final Map<String, Object> r = jdbcTemplate.queryForMap(sql, username, username, 29                    username); 30            if (valid(username, password, r, passwordEncoder) == true) { 31                 /** 32                   * 对旧用户数据更换加密方式,且重新生成密码 33                 */ 34                if (Mcrypt.MD5.equals(r.get("algo")) == true) { 35                    modifyEncoder((String) r.get("username"), credentials.getPassword(), 36                            (String) r.get("salt")); 37                } 38                return ture; 39            } 40        } catch (IncorrectResultSizeDataAccessException e) { 41        } 42 43        (3) 44        try { 45            List<Map<String, Object>> result = jdbcTemplate.queryForList(sql, username, username, 46                    username); 47 48            if (result != null && result.size() > 0) { 49                for (Map<String, Object> r : result) { 50                    if (valid(username, password, r, passwordEncoder) == true) { 51                        /** 52                         * 对旧用户数据更换加密方式,且重新生成密码 53                         */ 54                        if (Mcrypt.MD5.equals(r.get("algo")) == true) { 55                            modifyEncoder((String) r.get("username"), credentials.getPassword(), 56                                    (String) r.get("salt")); 57                        } 58                        return true; 59                    } 60                } 61            } 62        } catch (IncorrectResultSizeDataAccessException e) { 63        } 64 65        return false; 66    } 67 68    private boolean valid(String username, String password, final Map<String, Object> object, 69            PasswordEncoder passwordEncoder) { 70        String salt = (String) object.get("salt"); 71        String algo = (String) object.get("algo"); 72 73        passwordEncoder.setAlgo(algo); 74 75        /** 76         * 如果加密方式为 "MD5",则为老的加密方式 77         * 旧系统中,密码为明文传输,在后端 MD5 加密的, 78         * 现在密码在前端进行了 MD5 加密传输,所以无需双重加密,而直接 encode(password+salt) 79         */ 80        if (Mcrypt.MD5.equals(algo) == true) { 81            password += salt; 82        } else { 83            passwordEncoder.setSalt(salt); 84        } 85 86        final String encodedPassword = passwordEncoder.encode(password); 87        return encodedPassword != null && encodedPassword.equalsIgnoreCase((String) object.get("password")); 88    } 89 90    private void modifyEncoder(final String username, final String password, final String salt) { 91        PasswordEncoder passwordEncoder = (PasswordEncoder) getPasswordEncoder(); 92 93        passwordEncoder.setAlgo(Mcrypt.SHA512); 94        passwordEncoder.setSalt(salt); 95 96        String sql = "UPDATE `member` SET `algo` = ?, `password` = ? WHERE `username` = ?"; 97        jdbcTemplate.update(sql, Mcrypt.SHA512, passwordEncoder.encode(password), username); 98    } 99}
  5. 密码加密配置(在添加 WEB-INF/deployerConfigContext.xml 添加)

    1<bean id="passwordEncoder" class="com.buession.cas.authentication.handler.PasswordEncoder" 2    p:characterEncoding="UTF-8" />

    PasswordEncoder.java

  6. 添加验证码验证消息(WEB-INF/classes/messages_xxx.properties)

    1required.captcha=验证码不能为空 2INVALID_CAPTCHA=验证码错误
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

CAS 实现站内单点登录及实现第三方 OAuth、OpenId 登录(二) - HelloWorld