SpringMVC + Spring Security+Ajax 实现自定义登录
自定义的用户需要实现UserDetails接口,Security这个框架不关心你的应用时怎么存储用户和权限信息的。只要取出来的时候把它包装成一个CustomUserDetails 对象就OK。
本文只包含关键代码:
springsecurity配置文件
<http>
<!-- 自定义登陆 -->
<form-login login-page="/login" />
<!-- 自定义登出 -->
<logout logout-url="/logout" logout-success-url="/"
invalidate-session="true" />
<csrf disabled="true" />
<custom-filter ref="customAuthenticationFilter"
before="FORM_LOGIN_FILTER" />
<headers>
<frame-options policy="SAMEORIGIN"/>
</headers>
</http>
<beans:bean id="customAuthenticationFilter"
class="com.xxx.cn.security.CustomAuthenticationFilter">
<beans:property name="authenticationManager"
ref="authenticationManager" />
<beans:property name="authenticationFailureHandler">
<beans:bean
class="com.xxx.cn.security.CustomAuthenticationFailureHandler">
</beans:bean>
</beans:property>
<beans:property name="authenticationSuccessHandler"
ref="customAuthenticationSuccessHandler" />
</beans:bean>
定义认证成功和失败时候的处理:
认证成功
CustomAuthenticationFailureHandler.java
import java.io.IOException;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import com.xyz.cn.core.ResultGenerator;
import com.xyz.cn.security.dao.SpringSecurityMapper;
import com.xyz.cn.utils.WebUtil;
import es.moki.ratelimitj.core.limiter.request.RequestLimitRule;
import es.moki.ratelimitj.core.limiter.request.RequestRateLimiter;
import es.moki.ratelimitj.inmemory.request.InMemorySlidingWindowRequestRateLimiter;
@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
//规则定义 5分钟内五次
Set<RequestLimitRule> rules = Collections
.singleton(RequestLimitRule.of(Duration.of(5, ChronoUnit.MINUTES),5));
RequestRateLimiter limiter = new InMemorySlidingWindowRequestRateLimiter(rules);
@Autowired
private SpringSecurityMapper securityMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
String username = request.getParameter("username");
boolean reachLimit = limiter.overLimitWhenIncremented(username);
if(reachLimit) {
securityMapper.selectByUserName(username);
WebUtil.genResult(response, ResultGenerator.genFailResult("账号已锁定,请五分钟后再试"));
}else {
if(exception instanceof LockedException) {
WebUtil.genResult(response, ResultGenerator.genFailResult("账号已锁定,请五分钟后再试"));
}else {
WebUtil.genResult(response, ResultGenerator.genFailResult("账号或密码错误"));
}
}
}
}
认证失败
CustomAuthenticationSuccessHandler.java
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import com.xyz.cn.core.ResultGenerator;
import com.xyz.cn.dao.UserMapper;
import com.xyz.cn.domain.User;
import com.xyz.cn.utils.WebUtil;
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private UserMapper userMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal();
HttpSession session = request.getSession();
session.setAttribute("userId", customUserDetails.getId());
session.setAttribute("username", customUserDetails.getUsername());
//登陆次数+1
User user = userMapper.selectByPrimaryKey(customUserDetails.getId());
user.setLastLogin(new Date());
user.setLastLogin(new Date());
userMapper.updateByPrimaryKey(user);
System.out.println("成功");
if (customUserDetails.getRole().equals("ROLE_ADMIN")) {
WebUtil.genResult(response,ResultGenerator.genSuccessResult("登陆成功","ROLE_ADMIN"));
} else if (customUserDetails.getRole().equals("ROLE_USER")) {
WebUtil.genResult(response,ResultGenerator.genSuccessResult("登陆成功","ROLE_USER"));
} else if (customUserDetails.getRole().equals(null)) {
WebUtil.genResult(response,ResultGenerator.genFailResult("无法认证,请重新登录","anonymousUser"));
} else {
WebUtil.genResult(response,ResultGenerator.genFailResult("登录失败","anonymousUser"));
throw new RuntimeException();
}
}
}
WebUtil.genResult();
public static void genResult(final HttpServletResponse response,final Result<?> result) {
String json = new Gson().toJson(result);
response.setContentType("application/json;charset=utf-8");
PrintWriter out = null;
try {
out = response.getWriter();
} catch (IOException e) {
e.printStackTrace();
}finally {
out.write(json);
out.flush();
out.close();
}
}
需引入com.google.gson.Gson
Gson
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
ajax部分
前台使用的是LayUI
layui.use(['index','layer','form'], function () {
layui.admin.initTheme(11);
var layer = layui.layer;
var form = layui.form;
form.on('submit(login)',function(data){
var loadIndex = layer.msg('正在加载', { icon: 16, time: false, shade: 0.4 });
$.ajax({
type: "POST",
url: "${rc.contextPath}/login",
data:{'username':$('#LAY-user-login-username').val(),'password':$('#LAY-user-login-password').val()},
traditional:true,
dataType: "json",
success:function(res){
layer.close(loadIndex);
if( res.code == 200){
layer.msg("登录成功", {icon: 6,time: 1000,shade:0.4},function() {
window.location.href = '${rc.contextPath}/';
});
}else if (res.code === 400) {
layer.msg(res.message, {icon: 2,anim:6});
}else if(res.code == 500){
layer.msg(res.errorMsg,{icon:5});
}else{
layer.msg("抱歉程序出现异常,请等待修复",{icon:0,anim:6});
}
},error:function(data){
layer.close(loadIndex);
layer.msg("无法连接服务器,请检查网络连接",{icon:0,anim:6,shade:0.4});
}
});
return false;
})
})