signed

QiShunwang

“诚信为本、客户至上”

SpringMVC + Spring Security+Ajax 实现自定义登录

2021/3/21 3:38:43   来源:

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;
            })
            
        })