spring security3 ajax,Spring Boot+Spring Security+Ajax 实现自定义登录

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

@Slf4j

public 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 extends GrantedAuthority> 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")

@Slf4j

public 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)

// @Slf4j

public 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")

@Slf4j

public 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")

@Slf4j

public 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

登录页面:

x1.PNG

用户不存在:

d1.PNG

密码错误:

到.PNG

核心在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了。

你可能感兴趣的:(spring,security3,ajax)