【SSM】Kisso实用教程(二)
https://www.jianshu.com/p/4026edd340bb
Maven依賴項
/pom.xml
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus</artifactId><version>1.2.12</version> </dependency> <dependency><groupId>com.baomidou</groupId><artifactId>kisso</artifactId><version>3.6.6</version> </dependency> <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.9</version> </dependency> <dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.54</version> </dependency>Spring MVC設(shè)置
/resource/spring-mvc.xml
<!-- kisso 注入初始化,也支持使用 web.xml 初始化 --> <bean id="kissoInit" class="com.baomidou.kisso.web.WebKissoConfigurer" init-method="initKisso"><property name="ssoPropPath" value="sso.properties" /><!-- 測試模式 ,不同環(huán)境配置選擇設(shè)置 --><property name="runMode" value="test_mode" /><!-- 此處可以注入 SSOConfig 配置屬性,也可以定義自己的 kisso 插件,基礎(chǔ) SSOPlugin 抽象類。<property name="pluginList"><list><bean name="com.xxxx.MyPlugin"></list></property>--></bean><mvc:interceptors><!-- SSO 攔截器 --><!-- path 對所有的請求攔截使用/**,對某個模塊下的請求攔截使用:/myPath/* --><mvc:interceptor><mvc:mapping path="/user/*" /><mvc:mapping path="/permission/*" /><bean class="com.baomidou.kisso.web.interceptor.SSOSpringInterceptor" /></mvc:interceptor> </mvc:interceptors>Kisso設(shè)置
/resource/sso.properties
################ SSOConfig file ################# sso.encoding=utf-8 sso.secretkey=30eb4892122c45fd0f sso.cookie.name=uid sso.cookie.domain=.vcap.me sso.login.url=http://ssm.vcap.me:8080/ssm/user/tologin或者
################ SSOConfig file ################# sso.encoding=utf-8 sso.secretkey=30eb4892122c45fd0f sso.cookie.name=uid sso.cookie.domain=127.0.0.1 sso.login.url=http://127.0.0.1:8080/ssm/user/tologindomain不能為localhost,可修改hosts使用自定義域名。
User映射設(shè)置
/mapper/userMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.mapper.UserMapper"><!-- 解決表名與字段不匹配 --><resultMap type="User" id="userResultMap"><result property="userid" column="userid" /><result property="username" column="username" /><result property="password" column="password" /></resultMap><!-- 查詢用戶是否存在 --><select id="checkUserByUsername" resultType="int" parameterType="java.lang.String">select count(1) from user WHERE username=#{username}</select><!-- 添加用戶 --><insert id="addUser" parameterType="User">insert into user(username,password) values(#{username}, #{password})</insert><!-- 獲取用戶信息 --><select id="getUserInfoByName" resultType="User" parameterType="User">select * from user WHERE username=#{username}</select><!-- 查詢所有用戶--><select id="findAllUser" resultType="User">select * from user</select> </mapper>登錄時,根據(jù)username,獲取User類。
加鹽密碼=MD5(用戶名+原密碼)
User映射接口
/mapper/UserMapper.java
public interface UserMapper {/*** 添加用戶* @param user 用戶* @return 修改的行數(shù)*/int addUser(User user);/*** 查詢用戶是否存在* @param username 用戶名* @return */int checkUserByUsername(String username);/*** 根據(jù)用戶名返回用戶信息* @param user 用戶名* @return 用戶信息*/List<User> getUserInfoByName(User user);/*** 查詢所有用戶的信息* @return 用戶信息的表*/List<User> findAllUser(); }Java Bean
/model/User.java
帶有mybatis-plus.jar提供的注解,用于導(dǎo)出SQL語句。
import com.baomidou.mybatisplus.annotations.TableField; import com.baomidou.mybatisplus.annotations.TableId; public class User {@TableField(exist = false)private static final long serialVersionUID = 1L;/** 主鍵ID */@TableIdprivate Long userid;private String username;private String password;;public User() {super();}public Long getId() {return userid;}public void setId(Long id) {this.userid = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}User服務(wù)接口
/service/UserService.java
注意:Controller中帶有Autowired注解的字段必須為接口。
public interface UserService {/*** 添加用戶* @param user 用戶* @return 修改的行數(shù)*/int addUser(User user);/*** 查詢用戶是否存在* @param username 用戶名* @return */boolean checkUserByUsername(String username);/*** 檢查用戶名和密碼是否合法* @param user 登錄信息* @return 成功則返回id,失敗返回-1*/long validUserAndPassword(User user);/*** 查詢所有用戶的信息* @return 用戶信息的表*/List<User> findAllUser();User服務(wù)實現(xiàn)
/service/impl/UserServiceImpl.java
@Service @Transactional public class UserServiceImpl implements UserService {@Resourcepublic UserMapper userMapper;@Overridepublic int addUser(User user) {int userid = userMapper.addUser(user);return userid;}@Overridepublic boolean checkUserByUsername(String username) {return userMapper.checkUserByUsername(username) == 1;}@Overridepublic long validUserAndPassword(User user) {List<User> users = userMapper.getUserInfoByName(user);if (users.isEmpty()) {return -1;// 不存在}User info = users.get(0);if (SaltEncoder.md5SaltValid(user.getUsername(), info.getPassword(), user.getPassword())) {return info.getId();} else {return -1;// 不存在}}@Overridepublic List<User> findAllUser() {return userMapper.findAllUser();} }用戶注冊
/controller/UserController.java
@Login(action = Action.Skip) @RequestMapping(value = "/reg", method = RequestMethod.POST) public @ResponseBody Map<String, Object> addUser(@RequestParam(value = "username") String username,@RequestParam(value = "password") String password) {Map<String, Object> map = new HashMap<String, Object>();if (userService.checkUserByUsername(username)) {User user = new User();user.setUsername(username);user.setPassword(SaltEncoder.md5SaltEncode(username, password));int id = userService.addUser(user);logger.debug(String.format("add user: id=%d name=%s", id, username));map.put("code", "200");map.put("msg", "注冊成功!");} else {logger.warn(String.format("conflict user: name=%s", username));map.put("code", "400");map.put("msg", "用戶已存在!");}return map;}SaltEncoder.md5SaltEncode(登錄名,原密碼)=> 返回哈希密碼
用戶登錄
/controller/UserController.java
@Login(action = Action.Skip) @RequestMapping(value = "/login", method = RequestMethod.POST) public @ResponseBody Map<String, Object> login(@RequestParam(value = "username") String username,@RequestParam(value = "password") String password,@RequestParam(value = "verify") String verify) {Map<String, Object> map = new HashMap<String, Object>();String verifyCode = String.valueOf(request.getSession().getAttribute("verify"));if (!verifyCode.equalsIgnoreCase(verify)) {map.put("code", "400");map.put("msg", "驗證碼錯誤");return map;}request.getSession().removeAttribute("verify");/*** 生產(chǎn)環(huán)境需要過濾sql注入*/WafRequestWrapper req = new WafRequestWrapper(request);String username_ = req.getParameter("username");String password_ = req.getParameter("password");User user = new User();user.setUsername(username_);user.setPassword(password_);long userid = userService.validUserAndPassword(user);if (userid != -1) {logger.debug(String.format("login success: name=%s password=%s", username_, password_));map.put("code", "200");map.put("msg", "登錄成功!");/** authSSOCookie 設(shè)置 cookie 同時改變 jsessionId*/SSOToken st = new SSOToken(request);st.setId(userid);st.setUid(username_);st.setType(1);// 記住密碼,設(shè)置 cookie 時長 1 天 = 86400 秒 【動態(tài)設(shè)置 maxAge 實現(xiàn)記住密碼功能】/** String rememberMe = req.getParameter("rememberMe"); if* ("on".equals(rememberMe)) {* request.setAttribute(SSOConfig.SSO_COOKIE_MAXAGE, 86400); }*/request.setAttribute(SSOConfig.SSO_COOKIE_MAXAGE, -1);//瀏覽器關(guān)閉自動刪除cookieSSOHelper.setSSOCookie(request, response, st, true);} else {logger.warn(String.format("wrong login: name=%s password=%s", username_, password_));map.put("code", "400");map.put("msg", "您輸入的帳號或密碼有誤");}return map;}登錄的邏輯:
注意點:
-
為什么是uid? sso.properties中的sso.cookie.name項
-
怎么加密?密鑰在sso.properties中的sso.secretkey
驗證機(jī)制
控制器
/controller/UserController.java
/*** 驗證碼 (注解跳過權(quán)限驗證)*/ @Login(action = Action.Skip) @ResponseBody @RequestMapping("/verify") public void verify() {try {String verifyCode = CaptchaUtil.outputImage(response.getOutputStream());request.getSession().setAttribute("verify", verifyCode);//把驗證碼存入sessionlogger.debug(String.format("verify code: %s", verifyCode));} catch (IOException e) {e.printStackTrace();} }注意點:
- 地址是 #{controller}/verify,設(shè)為Action.Skip,因為任何人都可以獲取驗證碼,不寫則默認(rèn)為Action.Normal啟用認(rèn)證。
- 將驗證碼的明文存入Session中,待驗證登錄時取出。
繪圖
/com/utils/CaptchaUtil.java
public class CaptchaUtil {public static String outputImage(OutputStream out) throws IOException {ConfigurableCaptchaService cs = new ConfigurableCaptchaService();//驗證碼寬高cs.setWidth(85);cs.setHeight(35);//設(shè)置 6 位自適應(yīng)驗證碼// AdaptiveRandomWordFactory arw = new AdaptiveRandomWordFactory();// arw.setMinLength(6);// arw.setMaxLength(6);// cs.setWordFactory(arw);//字符大小設(shè)置RandomFontFactory rf = new RandomFontFactory();rf.setMinSize(25);rf.setMaxSize(28);cs.setFontFactory(rf);//文本渲染// cs.setTextRenderer(new RandomYBestFitTextRenderer());//設(shè)置一個單一顏色字體cs.setColorFactory(new SingleColorFactory(new Color(59, 162, 9)));// cs.setFilterFactory(new CurvesRippleFilterFactory(cs.getColorFactory()));//圖片濾鏡設(shè)置ConfigurableFilterFactory filterFactory = new ConfigurableFilterFactory();List<BufferedImageOp> filters = new ArrayList<BufferedImageOp>();//擺動干擾WobbleImageOp wio = new WobbleImageOp();wio.setEdgeMode(AbstractImageOp.EDGE_CLAMP);wio.setxAmplitude(2.0);wio.setyAmplitude(1.0);filters.add(wio);//曲線干擾// CurvesImageOp cio = new CurvesImageOp();// cio.setColorFactory(new SingleColorFactory(new Color(59, 162, 9)));// cio.setEdgeMode(AbstractImageOp.EDGE_ZERO);// cio.setStrokeMax(0.3f);// cio.setStrokeMin(0.1f);// filters.add(cio);filterFactory.setFilters(filters);cs.setFilterFactory(filterFactory);//橢圓形干擾背景// cs.setBackgroundFactory(new OvalNoiseBackgroundFactory(7));//線形干擾背景cs.setBackgroundFactory(new LineNoiseBackgroundFactory(37));//輸出驗證圖片return EncoderHelper.getChallangeAndWriteImage(cs, "png", out);} }HTML
/WebContent/jsp/login.jsp
引入js / html
<script src="${js_root}/js/jquery-1.11.1.js"></script> <script src="${js_root}/js/jquery.validate.min.js"></script> <script src="${js_root}/js/messages_zh.js"></script>gup取參函數(shù) / js
function gup(name) {name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");var regexS = "[\\?&]" + name + "=([^&#]*)";var regex = new RegExp(regexS);var results = regex.exec(location.pathname);if (results == null) {return location.pathname;} else {return results[1];}}初始化驗證 / js
$(document).ready(function() {// validate the comment form when it is submitted$("#signupForm").validate({rules : {username : {required : true,minlength : 2,},password : {required : true,minlength : 6},verify : {required : true,minlength : 4}},messages : {username : {required : "請輸入用戶名",minlength : "用戶名至少由兩個字符組成"},password : {required : "請輸入密碼",minlength : "密碼長度不能小于 6 個字符"},verify : {required : "請輸入驗證碼",minlength : "驗證碼長度為4個字符"}}});});設(shè)置提交方式 / js
$.validator.setDefaults({submitHandler : function() {$.post(// 接收數(shù)據(jù)的頁面'login',// 傳給后臺的數(shù)據(jù),多個參數(shù)用&連接或者使用json格式數(shù)據(jù):{a:'value1',b:'value2'}{username : $("#username").val(),password : $("#password").val(),verify : $("#verify").val()}, function(data) {if (data.code == '200') {alert("msg: " + data.msg + "\n" + "即將跳轉(zhuǎn)。");location.href = gup("ReturnURL");} else if (data.code == '400') {alert(data.msg);location.reload();}},// 默認(rèn)返回字符串,設(shè)置值等于json則返回json數(shù)據(jù)'json').error(function() {alert("登錄失敗,請稍后再試。");});}});設(shè)置表單 / html
<form class="cmxform" id="signupForm" method="post" action="login"><fieldset><legend>請輸入你的用戶名和密碼</legend><p><label for="cusername">用戶名</label> <input id="username"name="username" type="text"></p><p><label for="cpassword">密碼</label> <input id="password"name="password" type="password"></p><p><label for="cverify">驗證碼</label> <input id="verify" name="verify"type="text"> <img id="verifyImg"onclick="javascript:this.src=('verify?reload='+(new Date()).getTime())"src="verify" width="85" height="35" alt="點擊查看驗證碼"></p><p><input class="reset" type="reset" value="重置"> <inputclass="submit" type="submit" value="登錄"></p></fieldset> </form>代碼驗證 / java
/controller/UserController.java
@Login(action = Action.Skip) @RequestMapping(value = "/login", method = RequestMethod.POST)public @ResponseBody Map<String, Object> login(@RequestParam(value = "username") String username,@RequestParam(value = "password") String password,@RequestParam(value = "verify") String verify) {Map<String, Object> map = new HashMap<String, Object>();String verifyCode = String.valueOf(request.getSession().getAttribute("verify"));if (!verifyCode.equalsIgnoreCase(verify)) {map.put("code", "400");map.put("msg", "驗證碼錯誤");return map;}request.getSession().removeAttribute("verify");// 其他登錄認(rèn)證機(jī)制...}登出
/controller/UserController.java
@RequestMapping(value = "/logout") public String logout() {/*** <p>* SSO 退出,清空退出狀態(tài)即可* </p>* * <p>* 子系統(tǒng)退出 SSOHelper.logout(request, response); 注意 sso.properties 包含 退出到* SSO 的地址 , 屬性 sso.logout.url 的配置* </p>*/SSOToken st = SSOHelper.getToken(request);if (st != null) {logger.debug(String.format("logout: id=%d, uid=%s", st.getId(), st.getUid()));}SSOHelper.clearLogin(request, response);return "redirect:/"; }觸發(fā)登出事件:利用即可。
注意點:使用SSOHelper.clearLogin(request, response)
重定向
/WebContent/jsp/login.jsp
添加地址取參函數(shù)
function gup(name) {name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");var regexS = "[\\?&]" + name + "=([^&#]*)";var regex = new RegExp(regexS);var results = regex.exec(location.pathname);if (results == null) {return location.pathname;} else {return results[1];} }跳轉(zhuǎn)回登錄前的頁面
Kisso攔截器將未授權(quán)訪問重定向至登錄頁,帶ReturnURL參數(shù),存放跳轉(zhuǎn)前地址,登錄成功后,自動跳回。
if (data.code == '200') {alert("msg: " + data.msg + "\n" + "即將跳轉(zhuǎn)。");location.href = gup("ReturnURL"); } else if (data.code == '400') {alert(data.msg);location.reload(); }注意點:
- 返回200時,為成功,跳轉(zhuǎn)
- 返回400時,為失敗,刷新頁面
登出的重定向
點擊鏈接登出時,服務(wù)器返回302重定向。
HTML
<p><a href="tologout">登出</a></p>JAVA
@RequestMapping(value = "/logout") public String logout() {// SSO清理工作// ...return "redirect:/"; }注意點:
- 適用ajax。瀏覽器中的js跳轉(zhuǎn),地址可以從服務(wù)器寫,如/ssm
- 適用a href。服務(wù)器的302、301跳轉(zhuǎn),Controller方法返回String,值為"redirect:path/to/redirect"
顯示用戶名
/WebContent/jsp/index.jsp
/WebContent/jsp/permission.jsp
HTML
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!-- other --> <p>${ userid },歡迎光臨!</p>JAVA
SSOToken st = SSOHelper.getToken(request); if (st != null) {request.setAttribute("userid", st.getUid()); } return "/index";注意點:
- 模版引擎除了JSTL外還有Velocity等。Velocity充分體現(xiàn)了的MVC思想。
- 顯示用戶名的流程。
MVC簡易流程:
常見問題
ContextLoader類不存在
項目 -> 屬性 -> Web Deployment Assembly
Add => Java Build Path Entries => Maven Dependencies
缺少類
修改pom.xml,然后Update Project。
常用解決辦法
- 清理Tomcat目錄
- 重啟Tomcat
- Classpath路徑問題,增加JRE、Tomcat、Maven、Web App Lib
- Web Module問題,在項目屬性中的Project Facets
- 修改容器名稱,即localhost:8080/???,項目屬性中的Web Project Settings
- Java文件錯誤,修改Java Compiler,即編譯器版本
- 注意文件名和路徑的大小寫
總結(jié)
以上是生活随笔為你收集整理的【SSM】Kisso实用教程(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【SSM】Kisso实用教程之验证码简单
- 下一篇: CentOS 7镜像下载和安装教程