tags: 从零开发项目javascript
我要将其弄成相似的登录,功能是要比较完善的。css
原本我是想作一步写一步的,可是发现这样文章就会太乱,由于要改的地方太多了。前面写过的,后边就被修改了。这样看起来太混乱了。所以我如今是写完代码才来补这篇记录的。尽量把当时的思路记录下来,而且捋一捋看一下有没有不足的地方。html
个人登录注册模块主要是使用邮箱,这部分以前是没有了解过的。所以去找了一点资料。想完成使用邮箱来激活用户的思路可看下面:java
blog.csdn.net/u013863751/…jquery
根据这个思路,我就建立数据库表了,数据库表的设计以下:git
设计完数据库表以后就去写前台页面了。由于登录注册这部分的Dao层并不难,就几个简单的方法。github
前台我使用的是Bootstrap为主,登录注册模块是使用bootstrap官网提供的模版。web
导航栏是使用以前看见过别人博客上的导航条:ajax
blog.csdn.net/iamcgt/arti…正则表达式
有了导航条和bootstrap官网提供的登录注册模版了,背景如今太过单薄了。我想到了一个开源的项目:github.com/VincentGarr…。因而下载下来改了一下。个人首页样式就有了。
particles.js参考教程:
使用时要注意的是:particles div中包含的内容须要绝对定位。
注册的时候须要作前台校验的。原本是想本身写正则表达式作校验的,后来发现有BootstrapValidation这样一个组件。因而就去下载使用了。
boostrapValidation参考教程:
bootstrapValidation有一个坑,**submitHandler提交方法只有0.45版本才有效(亲测)。**后来在寻找版本的时候发现了一个很是有用的网站:可以查询到多个版本,十分好用。
通过一顿整改,个人页面和验证效果以下:
作了前台的校验是不够的。由于有可能别人是知道咱们注册的url就直接对其进行访问了(绕过bootstrapValidation来注册),所以咱们还须要作后台校验。后台校验我使用的是SpringMVC的校验。也就是 Hibernate Validator(和Hibernate的ORM无关)。
要使用springMVC校验就要导入对应的maven坐标:
<!--spring Validation校验-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.1.0.CR2</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
复制代码
参考资料:
zhongfucheng.bitcron.com/post/spring…
在写配置文件的时候虽然idea提示是红色的,可是无妨,仍是能够或获取获得的。对其忽略就行
那么咱们的后台校验也已经完成了。
还有须要作校验的就是,若是用户已经注册过了,咱们就不能让它再注册了。为了达到更好的用户体检,在提交表单前使用ajax来作校验就好了
//邮箱去作后台惟一性校验
submitHandler: function (validator, form, submitButton) {
var $email = $("#userEmail").val();
console.log($email);
$.ajax({
url: path + "/user/validateEmail.do",
type: "post",
async: false,
data: {
"userEmail": $email
},
success: function (responseText) {
if (responseText == "noEmail") {
validator.defaultSubmit();
sweetAlert("请到您指定的邮箱完成激活");
} else {
sweetAlert("您的邮箱已注册过了");
}
},
error: function () {
sweetAlert("系统错误!");
}
});
}
复制代码
作这个惟一性校验其实就是比对数据库有没有该条邮箱的记录罢了。
在寻找资料的时候发现了sweetAlert这么一个组件,发现比浏览器自带的alert要好看得多。所以又去下载来用了。值得一提的是,我使用的环境是windows下载zip的方式,**在官网下载的css文件是自带有错的。**后来仍是去上面我说的那个网站直接找出它的css和js文件就行了。
资料以下:
效果以下:
搞了那么久,就剩下入数据库了。
/** * 先对数据进行校验,再注册 * * @param user * @return * @throws Exception */
@RequestMapping("/register.do")
public String register(@Validated User user, BindingResult bindingResult) throws Exception {
//若是参数不对,就直接返回注册页面
List<ObjectError> allErrors = bindingResult.getAllErrors();
if (allErrors != null && allErrors.size() > 0) {
return "redirect:/goURL/user/toRegister.do";
}
//对密码进行加密md5(密码+salt)后才存到数据库中
userService.encryptedPassword(user);
userService.insert(user);
//提示用户发送了邮件,让用户激活帐户
String url = getProjectPath() + "/user/activate.do?userId=" + user.getUserId();
emailService.sendEmail(user, "注册", url);
return "redirect:/common/countDown.html";
}
复制代码
首次插入进数据库的时候,激活码默认值是0,还有uuid生成
<!--insert被自定义了。-->
<insert id="insert" parameterType="zhongfucheng.entity.User" >
<selectKey keyProperty="userId" order="BEFORE" resultType="string">
select uuid()
</selectKey>
insert into table_user (user_id, user_nickname, user_password,
user_email, acti_state, acti_code,
salt,token_exptime)
values (#{userId}, #{userNickname,jdbcType=VARCHAR}, #{userPassword,jdbcType=VARCHAR},
#{userEmail,jdbcType=VARCHAR}, 0, uuid(),
#{salt},now())
</insert>
复制代码
对密码加密也很简单:生成一个随机数做为salt,使用md5(用户传递进来的密码+salt)
发送邮件就涉及到了javaMail了,javaMail以前是没有接触过的。因而就去找了几篇资料:
我是使用qq邮箱去发送邮件的:要去qq邮箱申请受权码才能发送
在看上边资料的时候,发现邮件其实用freemarker来作模版会很不错(由于邮箱的只有少部份内容是变的),因而又去找freemarker与spring整合的资料:
上诉的maven坐标:
<!-- Javamail与Spring-context-support support包也与freemarker整合 -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<!--freemarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.18</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
复制代码
首先构建出发送邮件的模版数据、而后使用JavaMail发送带HTML格式的数据就好了。
/** * 邮件服务类,提供发送邮件的功能 */
@Service
public class EmailService {
@Autowired
private JavaMailSender mailSender;
@Autowired
private SimpleMailMessage simpleMailMessage;
/** * 使用mimeMessage发送的HTML格式的邮件。 * @param user * @param content * @throws Exception */
public void sendEmail(User user, String content,String url) throws Exception {
String returnText = createSendData(user, content,url);
// TODO 问题是出在发送邮件很慢 6086ms,解析freemarker才60ms 待优化
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
messageHelper.setFrom(simpleMailMessage.getFrom());
messageHelper.setSubject(simpleMailMessage.getSubject());
//接受人
messageHelper.setTo(user.getUserEmail());
//内容,是HTML格式
messageHelper.setText(returnText, true);
mailSender.send(mimeMessage);
}
/** * 使用freemarker建立要发送的邮件内容 * @param user 封装了要发送邮件的信息 * @param content 发送的目的是什么(一个模版、多种邮件) * @param url 操做的地址路径是什么 * @return HTML页面的内容 * @throws Exception */
public String createSendData(User user, String content,String url) throws Exception {
Map<String, Object> map = new HashMap();
map.put("nickName", user.getUserNickname());
map.put("content", content);
map.put("url", url);
map.put("encodeUrl", Base64Util.encodeData(url));
String returnText = new FreeMarkerUtils().returnText("email.ftl", map);
return returnText;
}
}
复制代码
freemarker在springmvc配置文件中的配置:
<!-- 同时开启json格式的支持 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 扫描全部的controller 可是不扫描service -->
<context:component-scan base-package="zhongfucheng">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
<!-- 配置Freemarker屬性文件路徑 -->
<bean id="freemarkerConfiguration" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:freemarker.properties"/>
</bean>
<!-- 配置freeMarker模板加載地址 -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<!-- 視圖解析器會在/WEB-INF/ftl/路徑下掃描視圖文件 -->
<property name="templateLoaderPath" value="/WEB-INF/ftl"/>
<!-- 设置页面中文乱码问题 -->
<property name="freemarkerSettings">
<props>
<prop key="defaultEncoding">UTF-8</prop>
</props>
</property>
<property name="freemarkerVariables">
<map>
<entry key="xml_escape" value-ref="fmXmlEscape"/>
</map>
</property>
</bean>
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>
<!-- 配置freeMarker視圖解析器 -->
<bean id="freemakerViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
<!-- 掃描路徑內全部以ftl結尾的文件 -->
<property name="viewNames">
<array>
<value>*.ftl</value>
</array>
</property>
<!-- 设置相关属性 -->
<property name="cache" value="true"/>
<property name="contentType" value="text/html; charset=UTF-8"/>
<property name="exposeRequestAttributes" value="true"/>
<property name="exposeSessionAttributes" value="true"/>
<property name="exposeSpringMacroHelpers" value="true"/>
<property name="requestContextAttribute" value="request"/>
<!-- 給視圖解析器配置優先級,你能够給以前jsp視圖解析器的值配為2 -->
<property name="order" value="1"/>
</bean>
<!--通用视图解析器-->
<bean id="viewResolverCommon" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.InternalResourceView
</value>
</property>
<property name="order" value="2"/>
</bean>
复制代码
在设置freemaker路径的时候发现本身对Java路径的相对路径、绝对路径、项目路径已有些混乱了。后来经过一篇资料又好好地整理一下:
不少时候咱们的项目路径在不一样机器上是不同的。所以要作到更好的通用性,能够将其在配置文件中配置起来。
提供一个工具类提取它就好了:
/** * 根据key读取配置文件的内容 * */
public class ReadPropertiesUtil {
public static String readProp(String key) {
InputStream in = ReadPropertiesUtil.class.getClassLoader().getResourceAsStream("system.properties");
Properties prop = new Properties();
try {
prop.load(in);
} catch (IOException e) {
e.printStackTrace();
}
return prop.getProperty(key);
}
}
复制代码
扯了这么一大堆,咱们的邮件已经可以发出去了
url连接使用了base64进行编码了,其实没什么,就是为了装个逼而已..
maven坐标:
<!--base64编码解码-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.6</version>
</dependency>
复制代码
base64工具类:
/** * base64的编码解码 */
public class Base64Util {
private static final String UTF_8 = "UTF-8";
/** * 对给定的字符串进行base64解码操做 */
public static String decodeData(String inputData) {
try {
if (null == inputData) {
return null;
}
return new String(Base64.decodeBase64(inputData.getBytes(UTF_8)), UTF_8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/** * 对给定的字符串进行base64加密操做 */
public static String encodeData(String inputData) {
try {
if (null == inputData) {
return null;
}
return new String(Base64.encodeBase64(inputData.getBytes(UTF_8)), UTF_8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}
复制代码
那么接下来就是点击链接激活帐户了,修改一下激活码值就好了。从上边咱们已经写到了:激活连接24小时内有效,若是超过了一天用户再点击激活码的时候,那么咱们就认为它无效,把数据库的记录删除了,让他从新注册。
/** * 激活帐户(实际上就是修改表字段的值) * * @param userId * @return * @throws Exception */
@RequestMapping("/activate.do")
public String activate(String userId) throws Exception {
User user = userService.selectByPrimaryKey(userId);
String title = "";
String content = "";
String subject = "";
if (user != null) {
//获得当前时间和邮件时间对比,24小时内
if (System.currentTimeMillis() - user.getTokenExptime().getTime() < 86400000) {
user.setActiState(User.ACTIVATION_SUCCESSFUL);
userService.updateByPrimaryKeySelective(user);
title = "用户激活页面";
subject = "用户激活";
content = "恭喜您成功激活帐户";
} else {
title = "激活失败页面";
subject = "用户激活";
content = "激活连接已超时,请从新注册";
//删除记录已便用户再次注册
userService.deleteByPrimaryKey(userId);
}
}
//根据模版生成页面,重定向到页面中
Map<String, Object> map = new HashedMap();
map.put("title", title);
map.put("content", content);
map.put("subject", subject);
map.put("path", getProjectPath());
createCommonHtml("promptPages.ftl", "promptPages.html", map);
return "redirect:/promptPages.html";
}
复制代码
好了,到如今为止注册模块就写完了。咱们来看一下实际的效果:
编写登录模块是用了我比较多的时间的,由于用了首次用了Shiro框架来进行作验证。当时候学的时候并非学得很深刻,因而出现了不少bug,改了几天才把它捋顺了。
登录页面和注册页面实际上是很是相似的,具体的步骤都和注册页面差很少。只不过我在登录页面中加入了一个验证码:
该验证码是动态的gif,是我以前看github项目的时候发现的。以为挺好看的就拿过来用了。要想使用它就要导入它的相关java类:
来源github项目:github.com/baichengzho…
我引入了Shiro框架来帮我作认证...
maven坐标:
<!--Shiro与Spring整合-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<!--Shiro与ehcache整合-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.3</version>
</dependency>
复制代码
首先来post一下个人基础shiro博文:
zhongfucheng.bitcron.com/category/sh…
想要在shiro认证以前可以校验验证码的话,那么就须要咱们去自定义表单过滤器了。
/** * 自定义一个表单过滤器的目的就是认证流程由本身控制 */
public class UserFormAuthenticationFilter extends FormAuthenticationFilter {
}
复制代码
当时候我重写了onAccessDenied()方法,在认证以前去校验验证码的正确性,而且使用ajax来进行提示用户是否有错误信息:
大体的错误代码以下:
/** * 用户登录,Shiro从Reaml中验证,返回JSON提示用户 * * @param request * @return * @throws Exception */
@RequestMapping("/login.do")
@ResponseBody
public Map<String, Object> login(HttpServletRequest request) throws Exception {
Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
//若是登录失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
//根据shiro返回的异常类路径判断,抛出指定异常信息
if (exceptionClassName != null) {
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
resultMap.put("message", "帐号不存在");
} else if (IncorrectCredentialsException.class.getName().equals(
exceptionClassName)) {
resultMap.put("message", "用户名/密码错误");
} else if ("captchaCodeError".equals(exceptionClassName)) {
resultMap.put("message", "验证码错误");
} else {
throw new Exception();//最终在异常处理器生成未知错误
}
} else {
resultMap.put("message", "登录成功");
//把用户信息写到session中
Subject subject = SecurityUtils.getSubject();
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
request.getSession().setAttribute("activeUser", activeUser);
}
return resultMap;
}
复制代码
$.ajax({
url: path + "/user/login.do",
type: "post",
async: false,
data: $("#loginForm").serialize(),
success: function (responseText) {
console.log(responseText);
if(responseText.message=="验证码错误"){
alert("验证码错误");
}else if(responseText.message=="帐号不存在") {
alert("帐号不存在");
}else if(responseText.message=="用户名/密码错误") {
alert("用户名/密码错误");
}else if(responseText.message=="登录成功") {
window.location.href = path + "/index.html";
}else {
console.log(responseText);
alert("未知错误");
}
},
error: function () {
alert("系统错误");
}
})
复制代码
在测试的时候就有很是怪异的想象:登录的时候,有时能够返回正常JSON的信息、有的时候直接不调用ajax(后台打断点并无进入后台),可是可以通过sucess方法返回一个页面的内容。
这就令我感到很是惊奇了,因而乎,我一直在搜索“为何ajax不调用、success方法却回调了”、”sucess回调方法返回一个页面“、”ajax常见错误“。显然,**我一直认为是ajax的问题,并无怀疑Shiro的认证流程。**在此方面花费我不少的时间,我怀疑过jquery的版本,还有其余方面的冲突.....
直到后来我就在想:为何有的时候JSON返回一个页面的内容呢???此时我想起Shiro的认证流程了。若是认证不经过,Shiro默认返回给login.do处理,若是验证经过,shiro默认返回上一级请求的url。
也就是说:**我在login.do中返回一个JSON是不合理的。**由于若是没有认证的话,Shiro默认是须要返回登录界面的,而我擅自修改为JSON了。因而就形成了奇怪的现象了。
那问题又来了,若是认证失败的话,为了作到更好的用户体验是须要实时地告诉用户哪里错了,而不是直接返回页面内容。用户不知道还会一脸懵逼。
由于login.do是专门处理异常的信息的,所以咱们可使用统一处理异常的方式去处理:
if (exceptionClassName != null) {
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
throw new UserException("帐号不存在");
} else if (IncorrectCredentialsException.class.getName().equals(
exceptionClassName)) {
throw new UserException("密码错误了");
} else if ("captchaCodeError".equals(exceptionClassName)) {
throw new UserException("验证码错误了");
} else {
throw new Exception();//最终在异常处理器生成未知错误
}
}
return "redirect:/goURL/user/toLogin.do";
复制代码
统一异常处理器:
/** * 统一异常处理类 */
public class SysException implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception ex) {
//输出异常
ex.printStackTrace();
String message = null;
UserException userException = null;
//若是ex是系统 自定义的异常,直接取出异常信息
if (ex instanceof UserException) {
userException = (UserException) ex;
} else {
//针对非UserException异常,对这类从新构形成一个UserException,异常信息为“未知错误”
userException = new UserException("未知错误");
}
message = userException.getMessage();
request.setAttribute("message", message);
try {
//根据模版生成页面,重定向到页面中
Map<String, Object> map = new HashedMap();
map.put("title", "错误页面");
map.put("content", message);
map.put("subject", "出错啦");
map.put("path", ReadPropertiesUtil.readProp("projectPath"));
FreeMarkerUtils markerUtils = new FreeMarkerUtils();
markerUtils.ouputFile("promptPages.ftl", "promptPages.html", map);
request.getRequestDispatcher("/promptPages.html").forward(request, response);
} catch (Exception e) {
e.printStackTrace();
}
return new ModelAndView();
}
}
复制代码
上面已经解决了提示错误信息的问题了。但是我以为不够好,由于错误信息跳转到别的页面了,用户须要从新回到登录页面进行注册,这个也太麻烦了吧。ps(要是我登录错误搞这么一个东西,我就认为这个是破网站...)。
因而乎,我就想在**怎么实时把错误信息返回给登录页面呢??ajax是否还能用呢??login方法是必定要返回一个页面的了。**试了不少无用的方法,在网上也找不到相关的方法,当时搜索关键字”Shiro返回错误信息“.....。
此时,我就在想ajax和Shiro是否能结合起来...后来去**搜索了一下”ajax和Shiro“**才知道网上也有人遇到我这种状况。
参考资料:
大体的思路就知道了:重写自定义表单过滤器的方法,判断是否为ajax请求来进行处理
期间找了不少相关的资料,每一个人的实现都良莠不齐。表单过滤器方法中的retrun true
和return false
也把我搞得一头雾水。最后,回归到搜索关键字“Shiro认证流程“,找到了一篇解决我问题的博文:
通过上面的资料和阅读了Shiro相关的源码,我基本能知道shiro认证流程了,下面是我画的一张流程图:
根据流程能够判断在验证码失败时若是是ajax请求返回JSON数据。若是登录失败,重写onLoginFailure方法,也判断是否为ajax请求。,
package zhongfucheng.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import zhongfucheng.entity.ActiveUser;
import zhongfucheng.utils.ReadPropertiesUtil;
import zhongfucheng.utils.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** * Created by ozc on 2017/10/27. */
/** * 自定义一个表单过滤器的目的就是认证流程由本身控制 */
public class UserFormAuthenticationFilter extends FormAuthenticationFilter {
/** * 只要请求地址不是post请求和不是user/login(处理登录的url),那么就返回登录页面上 * * @param request * @param response * @return * @throws Exception */
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
//判断是不是登录页面地址请求地址、若是不是那么重定向到controller的方法中
if (isLoginRequest(request, response)) {
if (isLoginSubmission(request, response)) {
//在提交给realm查询前,先判断验证码
if (WebUtils.validateCaptcha(httpRequest)) {
return executeLogin(request, response);
} else {
if (isAjax(httpRequest)) {
//这里要使用标准的JSON格式
WebUtils.printCNJSON("{\"message\":\"验证码错误\"}", httpServletResponse);
return false;
} else {
// 放行 allow them to see the login page ;)
httpRequest.setAttribute("shiroLoginFailure", "captchaCodeError");
return true;
}
}
} else {
// 放行 allow them to see the login page ;)
return true;
}
} else {
// TODO AJAX请求用户扩展。之后再补
if (isAjax(httpRequest)) {
//httpServletResponse.sendError(ShiroFilterUtils.HTTP_STATUS_SESSION_EXPIRE);
return false;
} else {
//返回配置的user/login.do,该方法会重定向到登录页面地址,再次发送请求给本方法
saveRequestAndRedirectToLogin(request, response);
}
return false;
}
}
/** * 认证成功,把用户认证信息保存在session中,判断是否为ajax请求 * @param token * @param subject * @param request * @param response * @return * @throws Exception */
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
//在跳转前将数据保存到session中
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
WebUtils.setValue2Session(httpRequest, "activeUser", activeUser);
//若是是ajax请求,那么咱们手动跳转
//若是不是ajax请求,那么由Shiro帮咱们跳转
if (isAjax(httpRequest)) {
WebUtils.printCNJSON("{\"message\":\"登录成功\"}", httpServletResponse);
} else {
//设置它跳转到首页路径,若是不设置它还会停留在登录页面。
String indexPath = ReadPropertiesUtil.readProp("projectPath") + "/index.html";
org.apache.shiro.web.util.WebUtils.redirectToSavedRequest(request, response, indexPath);
}
return false;
}
/** * 认证失败、若是ajax请求则返回json数据 * 若是非ajax请求、则默认返回给login.do处理异常 * @param token * @param e * @param request * @param response * @return */
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// 不是ajax请求,就按照源码的方式去干(返回异常给controller,controller处理异常)
if (!isAjax(httpServletRequest)) {
setFailureAttribute(request, e);
return true;
}
//是ajax请求,咱们返回json给浏览器
String message = e.getClass().getSimpleName();
if ("IncorrectCredentialsException".equals(message)) {
WebUtils.printCNJSON("{\"message\":\"密码错误\"}", httpServletResponse);
} else if ("UnknownAccountException".equals(message)) {
WebUtils.printCNJSON("{\"message\":\"帐号不存在\"}", httpServletResponse);
} else if ("captchaCodeError".equals(message)) {
WebUtils.printCNJSON("{\"message\":\"验证码错误\"}", httpServletResponse);
} else {
WebUtils.printCNJSON("{\"message\":\"未知错误\"}", httpServletResponse);
}
return false;
}
/** * 判断ajax请求 * * @param request * @return */
boolean isAjax(HttpServletRequest request) {
return (request.getHeader("X-Requested-With") != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With").toString()));
}
}
复制代码
值得一提的是:手动返回JSON格式数据、要是标准的JSON格式。不然会出错
参考资料:
login.do代码:
@RequestMapping("/login.do")
public String login(HttpServletRequest request, HttpServletResponse response) throws Exception {
/** * 若是在shiro配置文件中配置了authc的话,那么所点击的url就须要认证了才能够访问 * a:若是url是登录请求地址(user/login.do),不是post请求的话,流程是不会去Realm中的。那么会返回到该方法中,也就是会返回登录页面 * b:若是url是登录页面地址,是post请求的话,那么去realm中对比,若是成功了那么跳转到在表单过滤器中配置的url中 * * c:若是url不是登录页面地址,那么表单过滤器会重定向到此方法中,该方法返回登录页面地址。并记住是哪一个url被拦截住了 * d:用户填写完表单以后,会进入b环节,此时登录成功后跳转的页面是c环节记住的url。 */
//若是登录失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
//根据shiro返回的异常类路径判断,抛出指定异常信息
if (exceptionClassName != null) {
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
throw new UserException("帐号不存在");
} else if (IncorrectCredentialsException.class.getName().equals(
exceptionClassName)) {
throw new UserException("密码错误了");
} else if ("captchaCodeError".equals(exceptionClassName)) {
throw new UserException("验证码错误了");
} else {
throw new Exception();//最终在异常处理器生成未知错误
}
}
return "redirect:/goURL/user/toLogin.do";
}
复制代码
若是您以为这篇文章帮助到了您,能够给做者一点鼓励