servlet實現簡單的權限管理和敏感詞過濾功能
前言
JavaEE課要求用servlet和過濾器實現權限管理和敏感詞過濾功能,故有此文。
雖然早已知道瞭原理和用法,但是實際操作起來還是遇到瞭各種奇葩的情況。
一、如何實現權限管理
1.思路
當用戶訪問某個資源時,我們必須對其權限控制,所以得用到servlet中過濾器來對請求做一次預處理,判斷該用戶是否有權限訪問該資源,如果有則放行;如果沒有則返回拒絕訪問的通知。
那麼我們如何判斷該用戶是否有權限訪問呢?
這就要求我們在用戶登錄的時候保存其登錄狀態。
可我們知道http請求是無狀態的,即這次請求無法知道上一次請求的內容,那如何保存這個登錄狀態呢?
這就需要要用到Session/Cookie機制(這裡我就不多介紹瞭,感興趣的可以去百度,當然我在手把手教你用Java實現一套簡單的鑒權服務(SpringBoot,SSM)(萬字長文)這篇博文中說的很詳細瞭,感興趣的同學可以去看看)。
既然如此,那麼我們在登錄成功的時候就把用戶的信息存入到服務器的session中,等用戶下次訪問的時候,我們就知道是哪位用戶以及這位用戶有沒有權限訪問瞭。
至此我們便實現瞭簡單的權限控制。
但是這裡有個地方需要註意,就是過濾器在攔截時需要排除一些路徑,比如登錄頁面,比如靜態資源。這個有很多方法,我采取的是我認為比較簡單的方法,具體實現在下文。
2.代碼實現
①登錄頁面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>後臺管理-登陸</title> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="Access-Control-Allow-Origin" content="*"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="format-detection" content="telephone=no"> <link rel="stylesheet" href="/static/lib/layui-v2.6.3/css/layui.css" rel="external nofollow" media="all"> <!--[if lt IE 9]> <script src="https://cdn.staticfile.org/html5shiv/r29/html5.min.js"></script> <script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script> <![endif]--> <style> .main-body {top:50%;left:50%;position:absolute;-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);transform:translate(-50%,-50%);overflow:hidden;} .login-main .login-bottom .center .item input {display:inline-block;width:227px;height:22px;padding:0;position:absolute;border:0;outline:0;font-size:14px;letter-spacing:0;} .login-main .login-bottom .center .item .icon-1 {background:url(../static/images/icon-login.png) no-repeat 1px 0;} .login-main .login-bottom .center .item .icon-2 {background:url(../static/images/icon-login.png) no-repeat -54px 0;} .login-main .login-bottom .center .item .icon-3 {background:url(../static/images/icon-login.png) no-repeat -106px 0;} .login-main .login-bottom .center .item .icon-4 {background:url(../static/images/icon-login.png) no-repeat 0 -43px;position:absolute;right:-10px;cursor:pointer;} .login-main .login-bottom .center .item .icon-5 {background:url(../static/images/icon-login.png) no-repeat -55px -43px;} .login-main .login-bottom .center .item .icon-6 {background:url(../static/images/icon-login.png) no-repeat 0 -93px;position:absolute;right:-10px;margin-top:8px;cursor:pointer;} .login-main .login-bottom .tip .icon-nocheck {display:inline-block;width:10px;height:10px;border-radius:2px;border:solid 1px #9abcda;position:relative;top:2px;margin:1px 8px 1px 1px;cursor:pointer;} .login-main .login-bottom .tip .icon-check {margin:0 7px 0 0;width:14px;height:14px;border:none;background:url(../static/images/icon-login.png) no-repeat -111px -48px;} .login-main .login-bottom .center .item .icon {display:inline-block;width:33px;height:22px;} .login-main .login-bottom .center .item {width:288px;height:35px;border-bottom:1px solid #dae1e6;margin-bottom:35px;} .login-main {width:428px;position:relative;float:left;} .login-main .login-top {height:117px;background-color:#148be4;border-radius:12px 12px 0 0;font-family:SourceHanSansCN-Regular;font-size:30px;font-weight:400;font-stretch:normal;letter-spacing:0;color:#fff;line-height:117px;text-align:center;overflow:hidden;-webkit-transform:rotate(0);-moz-transform:rotate(0);-ms-transform:rotate(0);-o-transform:rotate(0);transform:rotate(0);} .login-main .login-top .bg1 {display:inline-block;width:74px;height:74px;background:#fff;opacity:.1;border-radius:0 74px 0 0;position:absolute;left:0;top:43px;} .login-main .login-top .bg2 {display:inline-block;width:94px;height:94px;background:#fff;opacity:.1;border-radius:50%;position:absolute;right:-16px;top:-16px;} .login-main .login-bottom {width:428px;background:#fff;border-radius:0 0 12px 12px;padding-bottom:53px;} .login-main .login-bottom .center {width:288px;margin:0 auto;padding-top:40px;padding-bottom:15px;position:relative;} .login-main .login-bottom .tip {clear:both;height:16px;line-height:16px;width:288px;margin:0 auto;} body {background:url(../static/images/loginbg.png) 0% 0% / cover no-repeat;position:static;font-size:12px;} input::-webkit-input-placeholder {color:#a6aebf;} input::-moz-placeholder {/* Mozilla Firefox 19+ */ color:#a6aebf;} input:-moz-placeholder {/* Mozilla Firefox 4 to 18 */ color:#a6aebf;} input:-ms-input-placeholder {/* Internet Explorer 10-11 */ color:#a6aebf;} input:-webkit-autofill {/* 取消Chrome記住密碼的背景顏色 */ -webkit-box-shadow:0 0 0 1000px white inset !important;} html {height:100%;} .login-main .login-bottom .tip {clear:both;height:16px;line-height:16px;width:288px;margin:0 auto;} .login-main .login-bottom .tip .login-tip {font-family:MicrosoftYaHei;font-size:12px;font-weight:400;font-stretch:normal;letter-spacing:0;color:#9abcda;cursor:pointer;} .login-main .login-bottom .tip .forget-password {font-stretch:normal;letter-spacing:0;color:#1391ff;text-decoration:none;position:absolute;right:62px;} .login-main .login-bottom .login-btn {width:288px;height:40px;background-color:#1E9FFF;border-radius:16px;margin:24px auto 0;text-align:center;line-height:40px;color:#fff;font-size:14px;letter-spacing:0;cursor:pointer;border:none;} .login-main .login-bottom .center .item .validateImg {position:absolute;right:1px;cursor:pointer;height:36px;border:1px solid #e6e6e6;} .footer {left:0;bottom:0;color:#fff;width:100%;position:absolute;text-align:center;line-height:30px;padding-bottom:10px;text-shadow:#000 0.1em 0.1em 0.1em;font-size:14px;} .padding-5 {padding:5px !important;} .footer a,.footer span {color:#fff;} @media screen and (max-width:428px) {.login-main {width:360px !important;} .login-main .login-top {width:360px !important;} .login-main .login-bottom {width:360px !important;} } </style> </head> <body> <div class="main-body"> <div class="login-main"> <div class="login-top"> <span>LayuiMini後臺登錄</span> <span class="bg1"></span> <span class="bg2"></span> </div> <form class="layui-form login-bottom" action="/login" method="post"> <div class="center"> <div class="item"> <span class="icon icon-2"></span> <input type="text" name="uname" lay-verify="required" placeholder="請輸入登錄賬號" maxlength="24"/> </div> <div class="item"> <span class="icon icon-3"></span> <input type="password" name="pwd" lay-verify="required" placeholder="請輸入密碼" maxlength="20"> <span class="bind-password icon icon-4"></span> </div> </div> <div class="tip"> <span class="icon-nocheck"></span> <span class="login-tip">保持登錄</span> <a href="javascript:" rel="external nofollow" class="forget-password">忘記密碼?</a> </div> <div class="layui-form-item" style="text-align:center; width:100%;height:100%;margin:0px;"> <button class="login-btn" type="submit" lay-submit="" lay-filter="login">立即登錄</button> </div> </form> </div> </div> <div class="footer"> ©版權所有 2014-2018 叁貳柒工作室<span class="padding-5">|</span><a target="_blank" href="http://www.miitbeian.gov.cn" rel="external nofollow" >粵ICP備16006642號-2</a> </div> <script src="/static/lib/layui-v2.6.3/layui.js" charset="utf-8"></script> <script> //原本想用json的post發送,結果發現後端數據得自己解析,為瞭降低難度,直接用form表單的post提交,這樣後端直接拿數據即可(不然還得解析Json數據) // layui.use(['form','jquery'], function () { // var $ = layui.jquery, // form = layui.form, // layer = layui.layer; // // // 登錄過期的時候,跳出ifram框架 // if (top.location != self.location) top.location = self.location; // // $('.bind-password').on('click', function () { // if ($(this).hasClass('icon-5')) { // $(this).removeClass('icon-5'); // $("input[name='pwd']").attr('type', 'password'); // } else { // $(this).addClass('icon-5'); // $("input[name='pwd']").attr('type', 'text'); // } // }); // // $('.icon-nocheck').on('click', function () { // if ($(this).hasClass('icon-check')) { // $(this).removeClass('icon-check'); // } else { // $(this).addClass('icon-check'); // } // }); // // // 進行登錄操作 // form.on('submit(login)', function (data) { // data = data.field; // if (data.uname == '') { // layer.msg('用戶名不能為空'); // return false; // } // if (data.pwd == '') { // layer.msg('密碼不能為空'); // return false; // } // $.ajax({ // url:'/login', // method:'post', // data:data, // dataType:'JSON', // success:function(res){ // if (res.msg==='登錄成功'){ // layer.msg('登錄成功', function () { // window.location = '../index.html'; // }); // }else { // layer.msg("登錄失敗"); // } // }, // error:function (data) { // } // }) ; // // // return false; // }); // }); </script> </body> </html>
因為上面我並沒有給出圖片,css等靜態資源,所以直接復制是沒有我這裡的效果的。
下面這樣也是可以的,
<!DOCTYPE html> <htmllang="en"> <head> <meta charset="UTF-8"> <title>用戶登錄</title> </head> <body> <form action="/login" method="post"> 用戶名:<input type="text" name="uname"> 密碼:<input type="password" name="pwd"> <input type="submit" value="login"> </form> </body> </html>
②登錄Servlet類
具體驗證的邏輯我這裡就不放瞭。這裡登錄成功會將用戶名存入session,並自動跳轉首頁。
package com.dreamchaser.loginTest.servlet; import com.dreamchaser.loginTest.mapper.UserMapper; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class LoginServlet extends HttpServlet { static UserMapper userMapper=UserMapper.getUserMapper(); @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String uname=req.getParameter("uname"); String pwd=req.getParameter("pwd"); ServletOutputStream outputStream = resp.getOutputStream(); String result; if (pwd.equals(userMapper.getPwdByName(uname))){ req.getSession().setAttribute("user",uname); resp.sendRedirect("/index.html"); }else { //響應 result="登錄失敗"; outputStream.write(result.getBytes()); } } }
③UserFilter過濾器
因為該過濾器配置的路徑是/*,即全部路徑,但是我們不想攔截一些路徑,比如登錄頁面,靜態資源,所以我們在操作前進行一次判斷,避免這些路徑的處理。
註:innit方法會將配置中排除的路徑讀取過來,當然你也可以寫在程序裡
package com.dreamchaser.loginTest.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; import java.util.List; public class UserFilter implements Filter { private List excludedUrls; @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { // 將req resp 轉為子接口的類型 HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)resp; String path = request.getServletPath(); if(!excludedUrls.contains(path)&&!(path.length()>8&&"/static/".equals(path.substring(0,8)))) { if (request.getSession().getAttribute("user")==null){ response.sendRedirect("/pages/login.html"); } } chain.doFilter(req, resp); } @Override public void init(FilterConfig filterConfig) throws ServletException { String excludePattern = filterConfig.getInitParameter("excludedUrls"); excludedUrls = Arrays.asList(excludePattern.split(",")); } @Override public void destroy() { } }
④web.xml配置項
init-param標簽裡存放的是排除在外的路徑,這個是自己定義的。
<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.dreamchaser.loginTest.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> <filter> <filter-name>UserFilter</filter-name> <filter-class>com.dreamchaser.loginTest.filter.UserFilter</filter-class> <init-param> <param-name>excludedUrls</param-name> <param-value>/pages/login.html,/register.html,/login</param-value> </init-param> </filter> <filter-mapping> <filter-name>UserFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
二、敏感詞過濾
1.思路
利用過濾器提前對請求裡要過濾的屬性進行處理,但是因為沒有setParameter方法(這是故意的,設計者就是不想讓我們直接修改),所以我們為瞭存放過濾後的信息,可以把過濾後的信息放在attribute中,然後servlet直接獲取即可。(當然也可以重寫一個Request類,那個比較麻煩,感興趣的自行百度)
2.代碼實現
①敏感詞過濾頁
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/word" method="post" style="margin-top: 300px"> 請輸入你要說的垃圾話:<br><input name="word" style="width: 500px" type="text"> <button type="submit"> 提交</button> </form> </body> </html>
②WordServlet類
從attribute中獲取過濾後的字符串後返回。
package com.dreamchaser.loginTest.servlet; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 需要敏感詞過濾的接口 */ public class WordServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //因為servlet並沒有setParameter方法(這是故意的),所以過濾後word無法通過getparameter來獲取 //所以一般有兩種方法一種把它放在attribute裡,一種重寫Request類,不過這會很麻煩 String word= String.valueOf(req.getAttribute("word")); ServletOutputStream outputStream = resp.getOutputStream(); String result="詞匯已凈化,請放心食用:"+word; outputStream.write(result.getBytes()); } }
③WordFilter過濾器類
靜態常量sensitiveWords中存放的是需要過濾的字符串,harmoniousWord是過濾後的詞匯。
這裡就是對需要過濾的借口提前進行處理,把敏感詞換成註入“***”這樣的字符串,然後存放在attribute中。
package com.dreamchaser.loginTest.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 敏感詞過濾攔截器 */ public class WordFilter implements Filter { static final String[] sensitiveWords={"敏感詞1","臟話","罵人"}; static final String harmoniousWord="***"; @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding("utf-8"); // 將req resp 轉為子接口的類型 HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)resp; String word=req.getParameter("word"); for (String s:sensitiveWords){ word=word.replaceAll(s,harmoniousWord); } request.setAttribute("word",word); chain.doFilter(req, resp); } }
web.xml配置項
<servlet> <servlet-name>WordServlet</servlet-name> <servlet-class>com.dreamchaser.loginTest.servlet.WordServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>WordServlet</servlet-name> <url-pattern>/word</url-pattern> </servlet-mapping> <filter> <filter-name>WordFilter</filter-name> <filter-class>com.dreamchaser.loginTest.filter.WordFilter</filter-class> </filter> <filter-mapping> <filter-name>WordFilter</filter-name> <url-pattern>/word</url-pattern> </filter-mapping>
三、效果展示
1.未登錄訪問其他資源
自動跳轉至登錄頁
2.密碼錯誤,登錄失敗
3.密碼正確,自動跳轉首頁
4.刷新首頁,不會跳轉
5.敏感詞過濾
處理後返回結果
註:代碼已開源至gitee,地址
到此這篇關於servlet實現簡單的權限管理和敏感詞過濾功能的文章就介紹到這瞭,更多相關servlet 權限管理和敏感詞過濾內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Servlet簡單實現登錄功能
- jsp+servlet實現簡單登錄頁面功能(附demo)
- 基於Cookie與Session的Servlet API會話管理操作
- 聊聊在Servlet中怎麼上傳文件
- Java實現註冊登錄跳轉