springboot +spring security4 自定義手機號碼+短信驗證碼登錄


spring security 默認登錄方式都是用戶名+密碼登錄,項目中使用手機+ 短信驗證碼登錄, 沒辦法,只能實現修改:

需要修改的地方:

1 、自定義 AuthenticationProvider 配置:

package com.ycmedia.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.ycmedia.constants.Constants;
import com.ycmedia.entity.Customer;
import com.ycmedia.entity.Role;
import com.ycmedia.service.UserService;
import com.ycmedia.utils.HttpRequest;

/**
 * @author 自定義驗證
 *
 */
@Component
public class YcAnthencationProder implements AuthenticationProvider {
    @Autowired
    private UserService userService;
    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    @Autowired
    private Environment env;

    @Override
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
//        CustomWebAuthenticationDetails details = (CustomWebAuthenticationDetails) authentication
//                .getDetails(); // 如上面的介紹,這里通過authentication.getDetails()獲取詳細信息
        // 用戶名
        String username = authentication.getName();
        // 驗證碼
        String password = (String) authentication.getCredentials();
        Customer user = userService.getUserByname(username);
        List<SimpleGrantedAuthority> auths = new ArrayList<>();
        //游客=》提示用戶去注冊
        if(user==null){
            //授權
            auths.add(new SimpleGrantedAuthority(Role.ROLE_TOURIST.toString()));
            auths.add(new SimpleGrantedAuthority(username));
            auths.add(new SimpleGrantedAuthority(password));
            return new UsernamePasswordAuthenticationToken(new Customer(), password,
                    auths);
        }else{
            //存在此用戶,調用登錄接口
            String data = HttpRequest.sendGet(env.getProperty("login.url"),
                    "mobile=" + username+"&smsCode="+password);
            JSONObject json = JSONObject.parseObject(data);
            if(json.getBoolean("success")==true){
                //驗證碼和手機號碼正確,返回用戶權限
                switch(user.getRole()){
                  case 0:auths.add(new SimpleGrantedAuthority(Role.ROLE_USER.toString()));
                  case 1:auths.add(new SimpleGrantedAuthority(Role.ROLE_CHANNEL.toString()));
                  case 2:auths.add(new SimpleGrantedAuthority(Role.ROLE_ADMIN.toString()));
                }
            }else{
                //驗證消息放到權限里面, 頁面提示
                auths.add(new SimpleGrantedAuthority(Role.ROLE_WRONGCODE.toString()));
                auths.add(new SimpleGrantedAuthority(username));
                auths.add(new SimpleGrantedAuthority(password));
            }
        }
        return new UsernamePasswordAuthenticationToken(user, password,
                auths);

    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}

==================這里把驗證碼當成password

security  安全中添加:

 

之前一直跑不通, 是因為在config 中引入我自己的 provider, 結果代碼跳不進去, 最后用spring 默認的provier  放進去,才可以。到現在還不明白

============================================================================

因為springsecurity 授權 順序是 1 :調用AuthenticationProvider  獲取Authentication  這個類存了用戶名, 密碼, 權限, 是否過期 鎖定等、然后才會調用 你的 controller 中的login方法, 

@SuppressWarnings("unchecked")
    @RequestMapping(value = { "/login", "/" })
    @ResponseBody
    public ModelAndView login(Model model)  {
        try {
            Authentication auth = SecurityContextHolder.getContext()
                    .getAuthentication();
            List<SimpleGrantedAuthority> auths=(List<SimpleGrantedAuthority>) auth.getAuthorities();
            //用戶存在。驗證碼錯誤,返回登錄頁面,提示文字
            if(auths.get(0).getAuthority().equals(Role.ROLE_WRONGCODE.toString())){
                model.addAttribute("msg", "驗證碼不正確");
                model.addAttribute("mobile", auths.get(1).getAuthority());
                model.addAttribute("code", auths.get(2).getAuthority());
                model.addAttribute("status", 1);
                return new ModelAndView("login");
            }
            //用戶不存在。跳轉到登錄頁面,提示文字
            else if(auths.get(0).getAuthority().equals(Role.ROLE_TOURIST.toString())){
                model.addAttribute("msg", "該用戶不存在");
                model.addAttribute("mobile", auths.get(1).getAuthority());
                model.addAttribute("code", auths.get(2).getAuthority());
                if(auths.get(1).getAuthority()!=null&&auths.get(2).getAuthority()!=null){
                model.addAttribute("status", 2);
                }
                return new ModelAndView("login");
            }
            if (auth instanceof AnonymousAuthenticationToken) {
                return new ModelAndView("login");
            } else {
                // 獲取用戶登錄權限詳細
                Object pinciba = auth.getPrincipal();
                if (pinciba instanceof UserDetails) {
                    UserDetails userDetail = ((UserDetails) pinciba);
                    model.addAttribute("username", userDetail.getUsername());
                    Customer u = userService
                            .getUserByname(userDetail.getUsername());
                    // 用戶角色
                    model.addAttribute("role", u.getRole());

                    String today = new DateTime().toString("yyyy-MM-dd");
                    // 今天收益
                    Double todayIncome = indexService.getTodayIncome(today);
                    // 今天總充值金額
                    Double buyInCome = indexService.getBuyIncome(today);
                    // 今天新增用戶數
                    Integer dayCount = indexService.findDayCount(today);
                    // 今天累計用戶數
                    Integer dayAllCount = indexService.findDayAllCount(today);
                    model.addAttribute("todayIncome",
                            todayIncome == null ? 0.00 + "元" : todayIncome
                                    + "元");
                    model.addAttribute("buyInCome",
                            buyInCome == null ? 0.00 + "元" : buyInCome + "元");
                    model.addAttribute("dayCount", dayCount == null ? 0 + "人"
                            : dayCount + "人");
                    model.addAttribute("dayAllCount", dayAllCount + "人");
                }

                // 登錄成功跳到主頁
                return new ModelAndView("home");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new ModelAndView("login");
        }

    }

 

===========================開發過程中發現一個問題, 就是springsecurity 的異常怎么在頁面接受, 百度了很多資料, 說用EL表達式 ${SPRING_SECURITY_LAST_EXCEPTION.message} 獲取, 可惜我不能獲取

主要是因為登錄的時候用form  有兩種 提交方式:

 1 form提交, 按鈕為submit , 這是springsecurity 默認配置, 就會先去調用  AuthenticationProvider  獲取權限再 調login方法, 但是回參不好在返回到頁面,

2 ajax 提交, 但是不走 AuthenticationProvider  ,

最后我把 錯誤信息放到 Authentication 。 它 會帶到我的controller 里面, 然后 返回個新頁面, 再 把 參數帶過去:

 

以下 是html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
    <meta name="description" content=""/>
    <meta name="author" content="ThemeBucket"/>
    <link rel="shortcut icon" href="#" type="image/png"/>
    <title>全民發布</title>
      <link href="/bootstrap/css/loginstyle.css" rel="stylesheet"/>
    <link href="/bootstrap/css/style-responsive.css" rel="stylesheet"/>
        <script src="/bootstrap/js/modernizr.min.js"></script>
    <link rel="stylesheet" href="/bootstrap/css/font-awesome.min.css"/>
    <script src="/plugins/jQuery/jquery-2.2.3.min.js"></script>
    <script src="/bootstrap/js/bootstrap.min.js"></script>
     <link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css"/>
     <link href="/bootstrap/css/bootstrapValidator.min.css" rel="stylesheet"/>
    <script src="/bootstrap/js/bootstrapValidator.js"></script>

  
</head>

<body class="login-body">

<div class="container">
       
    <form class="form-signin" th:action="@{/login}" method="post" id="loginForm">
    <input type="hidden" th:value="${mobile}" id="mobile"/>
        <input type="hidden" th:value="${msg}" id="msg"/>
        <input type="hidden" th:value="${code}" id="code"/>
        <div class="form-signin-heading text-center">
            <h1 class="sign-title">登錄</h1>
            <img src="http://apps.ycmedia.cn/qm/img/lable/120x80/00.png?ver=1" alt=""/>
        </div>
        <div class="login-wrap">
            <input id="username" name="username" type="text" placeholder="手機號碼" class="form-control" th:value="${mobile}"/>
              <span id="telephonenameTip"></span>
                 <div class="row"  th:if="${status==2}">
                                  <font color="red" th:text="${msg}" style="margin-left: 17px;"></font>         
               </div>
            <button type="button" class="btn btn-info" style="margin-left: 82%;margin-bottom: 12px;" id="getcode" >發送</button>
            <input id="" name="password" type="text" placeholder="驗證碼" class="form-control" th:value="${code}"/>
              <div class="row"  th:if="${status==1}">
                                  <font color="red" th:text="${msg}" style="margin-left: 17px;"></font>         
               </div>
            
            
            <button class="btn btn-lg btn-login btn-block" type="submit">
                <i class="fa fa-check"></i>
            </button>

            <div class="registration">
                還不是一個用戶?
                <a class=""  th:href="@{/registration}">
                    注冊
                </a>
            </div>
            <label class="checkbox">
                <input type="checkbox" value="remember-me"/> 記住我
                <span class="pull-right">
                    <a data-toggle="modal" href="#myModal"> 忘記密碼?</a>

                </span>
            </label>

        </div>

        <!-- Modal -->
        <div aria-hidden="true" aria-labelledby="myModalLabel" role="dialog" tabindex="-1" id="myModal" class="modal fade">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                        <h4 class="modal-title">Forgot Password ?</h4>
                    </div>
                    <div class="modal-body">
                        <p>Enter your e-mail address below to reset your password.</p>
                        <input type="text" name="email" placeholder="Email" autocomplete="off" class="form-control placeholder-no-fix"/>

                    </div>
                    <div class="modal-footer">
                        <button data-dismiss="modal" class="btn btn-default" type="button">Cancel</button>
                        <button class="btn btn-primary" type="button">Submit</button>
                    </div>
                </div>
            </div>
        </div>
        <!-- modal -->

    </form>

</div>
<script type="text/javascript">
window.onload = function () {
    $(':input','#loginForm')  
     .not(':button, :submit, :reset, :hidden')  
     .val('')  
     .removeAttr('checked')  
     .removeAttr('selected'); 
    //短信驗證碼  
    var InterValObj; //timer變量,控制時間    
    var count = 60; //間隔函數,1秒執行    
    var curCount;//當前剩余秒數    
    var code = ""; //驗證碼    
    var codeLength = 6;//驗證碼長度   

    $("#getcode").click(function () {
        //獲取輸入的手機號碼
        var phoNum = $("#username").val();
         var regrep=/^1[3|4|5|7|8][0-9]{9}$/;
         
         if(!regrep.test(phoNum)){
              $("#telephonenameTip").html('<font color="red">手機號碼格式不正確 </font>');
             return;
         }
   
        curCount = count;
        // 設置按鈕顯示效果,倒計時   
        $("#getcode").attr("disabled", "true");
        $("#getcode").val("請在" + curCount + "秒內輸入驗證碼");
        InterValObj = window.setInterval(SetRemainTime, 1000); // 啟動計時器,1秒執行一次    

        // 向后台發送處理數據    
        $.ajax({
            type: "get", // 用POST方式傳輸    
            dataType: "json", // 數據格式:JSON    
            url: "http://testi.xf120.com/qm/sms/send?mobile="+phoNum, // 目標地址    
            error: function (msg) {
                 $("#telephonenameTip").html('<font color="red">× 短信驗證碼發送失敗,請重新發送 </font>');
                alert(msg);
            },
            success: function (data) {
                 $("#telephonenameTip").html('<font color="green">√ 短信驗證碼已發到您的手機,請查收(15分鍾內有效)</font>');
              
            }
        });

    });

    //timer處理函數    
    function SetRemainTime() {
        if (curCount == 0) {
            window.clearInterval(InterValObj);// 停止計時器    
            $("#getcode").removeAttr("disabled");// 啟用按鈕    
            $("#getcode").val("重新發送驗證碼");
            code = ""; // 清除驗證碼。如果不清除,過時間后,輸入收到的驗證碼依然有效    
        } else {
            curCount--;
            $("#getcode").val("請在" + curCount + "秒內輸入驗證碼");
        }
    }
    
   
     

}

</script>


</body>

</html>

 

 

===================================================== 大概搞定了=============以下是效果圖

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM