博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot+Spring Security+Ajax 实现自定义登录
阅读量:2441 次
发布时间:2019-05-10

本文共 19724 字,大约阅读时间需要 65 分钟。

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

自定义的用户需要实现UserDetails接口,Security这个框架不关心你的应用时怎么存储用户和权限信息的。只要取出来的时候把它包装成一个UserDetails对象就OK。:

User.class:

package com.example.demo.model;import lombok.AllArgsConstructor;import lombok.Getter;import lombok.NoArgsConstructor;import lombok.Setter;import lombok.extern.slf4j.Slf4j;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList;import java.util.Collection;import java.util.List;@Setter@Getter@AllArgsConstructor@NoArgsConstructor@Slf4jpublic class User implements UserDetails{
private Integer id; private String username; private String password; private List
roles; // private String role; // private String status; // private boolean checkLockIsOrNot=true; public User(String username,String password){
this.username = username; this.password = password; } //不涉及用户角色,直接赋予管理员角色 @Override public Collection
getAuthorities() {
List
auths = new ArrayList<>(); auths.add(new SimpleGrantedAuthority("ROLE_ADMIN"); return auths; } //账户是否过期 @Override public boolean isAccountNonExpired() {
return true; } //账户是否锁定 @Override public boolean isAccountNonLocked() {
return true; } //密码是否过期 @Override public boolean isCredentialsNonExpired() {
return true; } //是否可用 @Override public boolean isEnabled() {
return true; }}

UserDetailsService接口用来加载用户信息,然后在loadUserByUsername方法中,构造一个User对象返回,这里实现这个接口来定义自己的Service

MyUserDetailService.class:

package com.example.demo.service;import com.example.demo.exception.ValidateCodeException;import com.example.demo.mapper.LockUserMapper;import com.example.demo.model.LockUser;import com.example.demo.model.User;import com.example.demo.model.UserLoginAttempts;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.social.connect.web.HttpSessionSessionStrategy;import org.springframework.social.connect.web.SessionStrategy;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.util.ArrayList;import java.util.List;/** * Created by linziyu on 2019/2/9. * * */@Component("MyUserDetailService")@Slf4jpublic class MyUserDetailService implements UserDetailsService{
@Autowired private UserService userService; // @Autowired // private LockUserMapper lockUserMapper; // private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
// List
grantedAuthorityList = new ArrayList<>(); // grantedAuthorityList.add(new GrantedAuthority() {
// @Override // public String getAuthority() {
// return "admin"; // } // }); User user = userService.findByUserName(s);//数据库查询 看用户是否存在 // ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); // HttpServletRequest request = servletRequestAttributes.getRequest(); // request.setAttribute("username",s); if (user == null){
throw new ValidateCodeException("用户不存在"); }// LockUser lockUser = lockUserMapper.findLockUserById(user.getId());// log.info("{}",lockUser);// if ( lockUser != null) {
// // throw new LockedException("LOCK");// user.setCheckLockIsOrNot(false);// } return user; }}

Spring Security的核心配置类:

因为只聚焦登录验证这个操作,其它功能就先注释掉了。

BrowerSecurityConfig.class:

package com.example.demo.config;import com.example.demo.filter.ValidateCodeFilter;import com.example.demo.handle.UserAuthenticationAccessDeniedHandler;import com.example.demo.handle.UserLoginAuthenticationFailureHandler;import com.example.demo.handle.UserLoginAuthenticationSuccessHandler;import com.example.demo.handle.UserLogoutSuccessHandler;import com.example.demo.service.MyUserDetailService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;import javax.annotation.Resource;import javax.sql.DataSource;/** * Created by linziyu on 2019/2/8. * *Spirng Security 核心配置类 * */@Configuration//标识为配置类@EnableWebSecurity//启动Spring Security的安全管理// @EnableGlobalMethodSecurity(securedEnabled = true)// @Slf4jpublic class BrowerSecurityConfig extends WebSecurityConfigurerAdapter {
private final static BCryptPasswordEncoder ENCODER = new BCryptPasswordEncoder(); // @Resource(name = "dataSource") // DataSource dataSource; @Bean public PasswordEncoder passwordEncoder(){
//密码加密类 return new BCryptPasswordEncoder(); } @Bean public MyUserDetailService myUserDetailService(){
return new MyUserDetailService(); }// @Bean// public PersistentTokenRepository persistentTokenRepository() {
// JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();// // 配置数据源// jdbcTokenRepository.setDataSource(dataSource);// // 第一次启动的时候自动建表(可以不用这句话,自己手动建表,源码中有语句的)// // jdbcTokenRepository.setCreateTableOnStartup(true);// return jdbcTokenRepository;// } @Autowired private UserLoginAuthenticationFailureHandler userLoginAuthenticationFailureHandler;//验证失败的处理类 @Autowired private UserLoginAuthenticationSuccessHandler userLoginAuthenticationSuccessHandler;//验证成功的处理类 // @Autowired // private UserLogoutSuccessHandler userLogoutSuccessHandler; // @Autowired // private UserAuthenticationAccessDeniedHandler userAuthenticationAccessDeniedHandler; @Override protected void configure(HttpSecurity http) throws Exception {
// ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter(); // http // .headers().frameOptions().sameOrigin();//设置弹出层 // http // .authorizeRequests() // .and() // .rememberMe() // .tokenRepository(persistentTokenRepository()) // .userDetailsService(myUserDetailService());; http .authorizeRequests() // .antMatchers("/admin/**","/setUserAdmin","/setUser","/deleteUserById") // .access("hasRole('ROLE_ADMIN')")//只有管理员才能访问 .antMatchers("/home","/static/**","/getAllUser","/register_page", "/register","/checkNameIsExistOrNot","/code/image")//静态资源等不需要验证 .permitAll()//不需要身份认证 .anyRequest().authenticated();//其他路径必须验证身份 http .formLogin() .loginPage("/login_page")//登录页面,加载登录的html页面 .loginProcessingUrl("/login")//发送Ajax请求的路径 .usernameParameter("username")//请求验证参数 .passwordParameter("password")//请求验证参数 .failureHandler(userLoginAuthenticationFailureHandler)//验证失败处理 .successHandler(userLoginAuthenticationSuccessHandler)//验证成功处理 .permitAll();//登录页面无需设置验证 // .and() // .rememberMe() // .tokenRepository(persistentTokenRepository()) // // 失效时间 // .tokenValiditySeconds(3600) // .userDetailsService(myUserDetailService()); // http // .logout() // .logoutSuccessHandler(userLogoutSuccessHandler)//登出处理 // .permitAll() // .and() // .csrf().disable() // .exceptionHandling().accessDeniedHandler(userAuthenticationAccessDeniedHandler);//无权限时的处理 } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailService()).passwordEncoder(new PasswordEncoder() {
@Override public String encode(CharSequence charSequence) {
return ENCODER.encode(charSequence); } //密码匹配,看输入的密码经过加密与数据库中存放的是否一样 @Override public boolean matches(CharSequence charSequence, String s) {
return ENCODER.matches(charSequence,s); } }); }}

定义认证成功和失败时候的处理:

UserLoginAuthenticationSuccessHandler.class:

package com.example.demo.handle;import com.example.demo.common.JsonData;import com.google.gson.Gson;import lombok.extern.slf4j.Slf4j;import org.springframework.security.core.Authentication;import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;import org.springframework.stereotype.Component;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;/** * Created by linziyu on 2019/2/9. * * 用户认证成功处理类 */@Component("UserLoginAuthenticationSuccessHandler")@Slf4jpublic class UserLoginAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
JsonData jsonData = new JsonData(200,"认证OK"); String json = new Gson().toJson(jsonData); response.setContentType("application/json;charset=utf-8"); PrintWriter out = response.getWriter(); out.write(json); out.flush(); out.close(); }}

UserLoginAuthenticationFailureHandler.class:

package com.example.demo.handle;import com.example.demo.common.DateUtil;import com.example.demo.common.JsonData;import com.example.demo.model.User;import com.example.demo.model.UserLoginAttempts;import com.example.demo.service.UserService;import com.google.gson.Gson;import lombok.extern.slf4j.Slf4j;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;import org.springframework.stereotype.Component;import javax.annotation.Resource;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;/** * Created by linziyu on 2019/2/9. * * 用户认证失败处理类 */@Component("UserLoginAuthenticationFailureHandler")@Slf4jpublic class UserLoginAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Resource private UserService userService; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// log.info("{}","认证失败"); // log.info("{}",exception.getMessage()); // String username = (String) request.getAttribute("username"); JsonData jsonData = null; if (exception.getMessage().equals("用户不存在")){
jsonData = new JsonData(402,"用户不存在"); } if(exception.getMessage().equals("Bad credentials")){
jsonData = new JsonData(403,"密码错误"); // String user_name =userService.findByUserNameAttemps(username); // if (user_name == null){
// String time = DateUtil.getTimeToString(); // UserLoginAttempts userLoginAttempts = new UserLoginAttempts(username,1,time); // userService.saveAttempts(userLoginAttempts); // } // if(userService.getAttempts(username) == 1){
// String time = DateUtil.getTimeToString(); // userService.setAttempts(username,time); // jsonData = new JsonData(403,"密码错误,你还有2次机会进行登录操作"); // } // else if(userService.getAttempts(username) == 3){
// User user = userService.findByUserName(username); // userService.LockUser(user.getId()); // jsonData = new JsonData(403,"最后一次尝试登陆失败,你已经被冻结了"); // } // else if (userService.getAttempts(username) ==2 ){
// String time = DateUtil.getTimeToString(); // userService.setAttempts(username,time); // jsonData = new JsonData(403,"密码错误,你还有1次机会进行登录操作"); // } } // if (exception.getMessage().equals("User account is locked")){
// jsonData = new JsonData(100,"LOCK"); // } String json = new Gson().toJson(jsonData);//包装成Json 发送的前台 response.setContentType("application/json;charset=utf-8"); PrintWriter out = response.getWriter(); out.write(json); out.flush(); out.close(); }}

前台页面使用的是LayUI

登录页面:

在这里插入图片描述

密码错误:

在这里插入图片描述

用户不存在:

在这里插入图片描述

核心在login.js:

/** * Created by linziyu on 2019/2/9. */// 登录处理layui.use(['form','layer','jquery'], function () {
var form = layui.form; var $ = layui.jquery; form.on('submit(login)',function (data) {
var username = $('#username').val(); var password = $('#password').val(); var remember = $('input:checkbox:checked').val(); var imageCode = $('#imgcode').val(); $.ajax({
url:"/login",//请求路径 data:{
"username": username,//字段和html页面的要对应 id和name一致 "password": password,//字段和html页面的要对应 // "remember-me":remember, // "imageCode": imageCode }, dataType:"json", type:'post', async:false, success:function (data) {
if (data.code == 402){
layer.alert("用户名不存在",function () {
window.location.href = "/login_page" }); } if (data.code == 403){
layer.alert(data.msg,function () {
window.location.href = "/login_page" }); } // if (data.code == 100){
// layer.alert("该用户已经被冻结,请联系管理员进行解冻",function () {
// window.location.href = "/login_page" // }); // } if(data.code == 200){
window.location.href = "/"; } // if (data.code == 101){
// layer.alert(data.msg,function () {
// window.location.href = "/login_page" // }); // } } }); })});

关于Ajax请求路径为什么是”/login“可以看源码UsernamePasswordAuthenticationFilter.class这是Spring Security Filter 中的第一个进行执行的Filter可以看到在第26行有默认的设置

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.springframework.security.web.authentication;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.security.authentication.AuthenticationServiceException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import org.springframework.util.Assert;public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username"; public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password"; private String usernameParameter = "username"; private String passwordParameter = "password"; private boolean postOnly = true; public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST")); } public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if(this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } else {
String username = this.obtainUsername(request); String password = this.obtainPassword(request); if(username == null) {
username = ""; } if(password == null) {
password = ""; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); this.setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } } protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(this.passwordParameter); } protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(this.usernameParameter); } protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); } public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null"); this.usernameParameter = usernameParameter; } public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null"); this.passwordParameter = passwordParameter; } public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly; } public final String getUsernameParameter() {
return this.usernameParameter; } public final String getPasswordParameter() {
return this.passwordParameter; }}

数据库操作就不啰嗦了,用的是Mybatis+Mysql组合,无非是CRUD了。

具体demo在我的github:

转载地址:http://bxcqb.baihongyu.com/

你可能感兴趣的文章
如何查找公共子字符串长度_如何在C中查找字符串的长度
查看>>
javascript运算符_JavaScript比较运算符
查看>>
字符串tostring_字符串toString()方法
查看>>
字符串方法中会修改原字符串_字符串padStart()方法
查看>>
字符串endsWith()方法
查看>>
number.isnan_Number isNaN()方法
查看>>
虚拟dom_虚拟DOM
查看>>
安卓录制视频不录制外界声音_我如何录制我的视频
查看>>
vue组件引入scss变量_如何将SCSS与Vue.js单个文件组件一起使用
查看>>
node.js删除文件_如何使用Node.js删除文件
查看>>
开发人员,学习营销
查看>>
webassembly_WebAssembly简介
查看>>
react useref_如何使用useRef React钩子
查看>>
axios 请求node_使用Axios的Node中的HTTP请求
查看>>
node http模块_Node http模块
查看>>
如何使用Hugo建立博客
查看>>
macos sqlite_如何在macOS上安装SQLite
查看>>
setimmediate_了解setImmediate()
查看>>
npm 语义化发布_使用npm的语义版本控制
查看>>
git可视化工具使用_使用Go可视化您本地的Git贡献
查看>>