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">×</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>
===================================================== 大概搞定了=============以下是效果圖