java ajax 登陆验证,Spring Security4实例(Java config版)——ajax登录,自定义验证
本文源码请看这里
首先添加起步依赖(如果不是springboot项目,自行切换为Spring Security依赖)
org.springframework.boot
spring-boot-starter-security
编写MyUserDetails类(在这里增加一个userId字段),实现UserDetails接口
public class MyUserDetails implements UserDetails{
private Integer userId;
private String username;
private String password;
private boolean enabled;
private Collection extends GrantedAuthority> authorities;
public MyUserDetails(Integer userId, String username, String password, boolean enabled) {
super();
this.userId = userId;
this.username = username;
this.password = password;
this.enabled = enabled;
}
public MyUserDetails(Integer userId, String username, String password, boolean enabled,
Collection extends GrantedAuthority> authorities) {
super();
this.userId = userId;
this.username = username;
this.password = password;
this.enabled = enabled;
this.authorities = authorities;
}
public Integer getUserId(){
return this.userId;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public String toString() {
return "MyUserDetails [userId=" + userId + ", username=" + username
+ ", password=" + password + ", enabled=" + enabled
+ ", authorities=" + authorities + "]";
}
}
编写UserDetailsServiceImpl类,实现UserDetailsService接口
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private final UserMapper userMapper;
private final RoleMapper roleMapper;
public UserDetailsServiceImpl(UserMapper userMapper, RoleMapper roleMapper) {
this.userMapper = userMapper;
this.roleMapper = roleMapper;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.getUserByUsername(username);
if (user != null) {
List authorities = new ArrayList();
List roleTypeList = roleMapper.listRoleTypeByUsername(username);
for (String roleType : roleTypeList) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + roleType));
}
return new MyUserDetails(user.getUserId(), user.getUsername(), user.getPassword(), true, authorities);
}
throw new UsernameNotFoundException("用户名 '" + username + "'没有找到!");
}
}
编写MyUsernamePasswordAuthenticationFilter类(在这里增加验证码校验功能),继承UsernamePasswordAuthenticationFilter
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
// 是否开启验证码功能
private boolean isOpenValidateCode = true;
public static final String VALIDATE_CODE = "validateCode";
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (isOpenValidateCode) {
checkValidateCode(request);
}
return super.attemptAuthentication(request, response);
}
protected void checkValidateCode(HttpServletRequest request) {
HttpSession session = request.getSession();
String sessionValidateCode = obtainSessionValidateCode(session);
sessionValidateCode = "1234";// 做个假的验证码;
// 让上一次的验证码失效
session.setAttribute(VALIDATE_CODE, null);
String validateCodeParameter = obtainValidateCodeParameter(request);
if (StringUtils.isEmpty(validateCodeParameter) || !sessionValidateCode.equalsIgnoreCase(validateCodeParameter)) {
throw new AuthenticationServiceException("验证码错误!");
}
}
private String obtainValidateCodeParameter(HttpServletRequest request) {
Object obj = request.getParameter(VALIDATE_CODE);
return null == obj ? "" : obj.toString();
}
protected String obtainSessionValidateCode(HttpSession session) {
Object obj = session.getAttribute(VALIDATE_CODE);
return null == obj ? "" : obj.toString();
}
}
编写配置类WebSecurityConfig继承WebSecurityConfigurerAdapter(下面注入的2个mapper就是mybatis的接口)
@Configuration
@EnableWebSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserMapper userMapper;
@Autowired
RoleMapper roleMapper;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 忽略静态文件
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/frame/**", "/img/**", "/css/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/login/**").permitAll()
// user权限可以访问的请求
.antMatchers("/security/user").hasRole("user")
// admin权限可以访问的请求
.antMatchers("/security/admin").hasRole("admin")
// SpEL表达式:需要拥有user权限,且进行了完全认证
.antMatchers("/user/account").access("hasRole('user') and isFullyAuthenticated()")
// 其他地址的访问均需验证权限(需要登录)
.anyRequest().authenticated().and()
// 添加验证码验证
.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class).exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login_page")).and()
// 指定登录页面的请求路径
.formLogin().loginPage("/login_page")
// 登陆处理路径
.loginProcessingUrl("/login").permitAll().and()
// 退出请求的默认路径为logout,下面改为signout,
// 成功退出登录后的url可以用logoutSuccessUrl设置
.logout().logoutUrl("/signout").logoutSuccessUrl("/login_page").permitAll().and()
// 关闭csrf
.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceImpl()).passwordEncoder(new Md5PasswordEncoder())
}
@Bean
public UserDetailsServiceImpl userDetailsServiceImpl() {
return new UserDetailsServiceImpl(userMapper, roleMapper);
}
@Bean
public MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
MyUsernamePasswordAuthenticationFilter myFilter = new MyUsernamePasswordAuthenticationFilter();
myFilter.setAuthenticationManager(authenticationManagerBean());
myFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
myFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
myFilter.setRememberMeServices(tokenBasedRememberMeServices());
return myFilter;
}
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new SimpleUrlAuthenticationSuccessHandler("/login/success");
}
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new SimpleUrlAuthenticationFailureHandler("/login/failure");
}
}
编写UserController进行一些业务处理和测试
@RestController
public class UserController {
private RequestCache requestCache = new HttpSessionRequestCache();
@RequestMapping(value = "/login_page", method = RequestMethod.GET)
public ModelAndView loginPage(HttpServletRequest request) {
if (HttpHelper.isAjaxRequest(request)) {
return new ModelAndView("/login/ajax");
} else {
return new ModelAndView("login.html");
}
}
@RequestMapping(value = "/login/success", method = RequestMethod.GET)
public Map loginSuccess(HttpServletRequest request, HttpServletResponse response) {
SavedRequest savedRequest = requestCache.getRequest(request, response);
String targetUrl = null;
if (savedRequest != null) {
targetUrl = savedRequest.getRedirectUrl();
}
Map result = new HashMap();
result.put("success", true);
result.put("targetUrl", targetUrl);
return result;
}
@RequestMapping(value = "/login/failure", method = RequestMethod.GET)
public Map loginFailure(HttpServletRequest request, HttpServletResponse response) {
AuthenticationException ae = (AuthenticationException) request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
Map result = new HashMap();
result.put("success", false);
result.put("message", ae.getMessage());
return result;
}
@RequestMapping(value = "/login/ajax", method = RequestMethod.GET)
public Map loginAjax() {
Map result = new HashMap();
result.put("success", false);
result.put("message", "you need login!");
return result;
}
@RequestMapping(value = "/security/user", method = RequestMethod.GET)
public Map securityUser(HttpServletRequest request) {
MyUserDetails user = UserUtil.getCurrentUser();
Map result = new HashMap();
StringBuilder userRole = new StringBuilder();
if (user != null) {
result.put("userId", user.getUserId());
result.put("userName", user.getUsername());
Collection extends GrantedAuthority> roleLst = user.getAuthorities();
for (GrantedAuthority sga : roleLst) {
userRole.append(sga.toString() + "; ");
}
}
result.put("userRole", userRole.toString());
result.put("message", "This message is only visible to the user");
return result;
}
@RequestMapping(value = "/security/admin", method = RequestMethod.GET)
public Map securityAdmin(HttpServletRequest request) {
MyUserDetails user = UserUtil.getCurrentUser();
Map result = new HashMap();
StringBuilder userRole = new StringBuilder();
if (user != null) {
result.put("userId", user.getUserId());
result.put("userName", user.getUsername());
Collection extends GrantedAuthority> roleLst = user.getAuthorities();
for (GrantedAuthority sga : roleLst) {
userRole.append(sga.toString() + "; ");
}
}
result.put("userRole", userRole.toString());
result.put("message", "This message is only visible to the admin");
return result;
}
@RequestMapping(value = "/user/account", method = RequestMethod.GET)
public Map getUserAcctunt(HttpServletRequest request) {
Map result = new HashMap();
result.put("message", "需要进行完整认证的请求(不是通过Remember-me功能进行的认证)");
return result;
}
}
还有个获取当前用户的工具类:
/**
* 用户工具类
*/
public class UserUtil {
/*
* 获取当前用户
*
* @return
*/
public static MyUserDetails getCurrentUser() {
MyUserDetails user = null;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = null;
if (authentication != null) {
principal = authentication.getPrincipal();
}
if (principal != null && principal instanceof MyUserDetails) {
user = (MyUserDetails) principal;
}
return user;
}
}
部分说明:
当没有登录的时候,访问一个受限制的页面或者一个ajax请求时,需要被重定向到登录页(请求方法.loginPage("/login_page")里定义的路径),因为如果"/login_page"直接返回一个页面的话,ajax是无法处理的,所以要先判断是否为ajax请求,如果是则返回一个错误标识,然后在页面进行后续处理,如果不是则直接返回登录页。
@RequestMapping(value = "/login_page", method = RequestMethod.GET)
public ModelAndView loginPage(HttpServletRequest request) {
if (HttpHelper.isAjaxRequest(request)) {
return new ModelAndView("/login/ajax");
} else {
return new ModelAndView("login.html");
}
}
@RequestMapping(value = "/login/ajax", method = RequestMethod.GET)
public Map loginAjax() {
Map result = new HashMap();
result.put("success", false);
result.put("message", "you need login!");
return result;
}
效果如图
当定义了自己的MyUsernamePasswordAuthenticationFilter时候,需要在定义bean的时候设置认证成功和认证失败的处理,如果没有设置认证失败的处理则会返回一个403的错误,下面分别定义了2个处理路径
@Bean
public MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
MyUsernamePasswordAuthenticationFilter myFilter = new MyUsernamePasswordAuthenticationFilter();
myFilter.setAuthenticationManager(authenticationManagerBean());
myFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
myFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
myFilter.setRememberMeServices(tokenBasedRememberMeServices());
return myFilter;
}
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new SimpleUrlAuthenticationSuccessHandler("/login/success");
}
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new SimpleUrlAuthenticationFailureHandler("/login/failure");
}
/**
* 如果是访问受限页面后,跳转到登录页的,则在targetUrl保存之前受限页面的路径,供页面调用
*
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/login/success", method = RequestMethod.GET)
public Map loginSuccess(HttpServletRequest request, HttpServletResponse response) {
SavedRequest savedRequest = requestCache.getRequest(request, response);
String targetUrl = null;
if (savedRequest != null) {
targetUrl = savedRequest.getRedirectUrl();
}
Map result = new HashMap();
result.put("success", true);
result.put("targetUrl", targetUrl);
return result;
}
/**
* 获取异常信息返回给页面
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/login/failure", method = RequestMethod.GET)
public Map loginFailure(HttpServletRequest request, HttpServletResponse response) {
AuthenticationException ae = (AuthenticationException) request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
Map result = new HashMap();
result.put("success", false);
result.put("message", ae.getMessage());
return result;
}
注意:
@Overide
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
@Autowired
public void whatever(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
以上两个方法只是写法不同,效果是一样的,主要有如下限制:
configure方法是重写所以需要继承 WebSecurityConfigurerAdapter类;
whatever方法意思就是方法名是什么并不重要(例如可以叫configureGlobal ),但是它需要:
类上有@EnableWebSecurity, @EnableWebMvcSecurity, @EnableGlobalMethodSecurity, 或@EnableGlobalAuthentication其中之一的注解;
包含AuthenticationManagerBuilder参数;
stackoverflow上有详细介绍,请看这里
Spring Security 4.1.0版本的文档上也有提及:
The name of the configureGlobal method is not important. However, it is important to only configure AuthenticationManagerBuilder in a class annotated with either @EnableWebSecurity, @EnableGlobalMethodSecurity, or @EnableGlobalAuthentication. Doing otherwise has unpredictable results.
java ajax 登陆验证,Spring Security4实例(Java config版)——ajax登录,自定义验证相关推荐
- spring 加载java类_在Spring中基于Java类进行配置的完整步骤
在Spring中基于Java类进行配置的完整步骤 发布于 2020-7-7| 复制链接 基于Java配置选项,可以编写大多数的Spring不用配置XML,下面 前言JavaConfig 原来是 Spr ...
- Java EE 6 VS Spring 3:Java EE杀死了Spring? 没门!
介绍 几天前,我在听Java Spotlight Podcast的第85集 . 在这次演讲中, Bert Ertman和Paul Bakker讨论了从Spring迁移到Java EE的问题. 基本上, ...
- Java EE 6 VS Spring 3:Java EE已经杀死了Spring? 没门!
介绍 几天前,我在听Java Spotlight Podcast的插曲85 . 在这次演讲中, Bert Ertman和Paul Bakker讨论了从Spring迁移到Java EE. 基本上,在他们 ...
- java小马哥springboot_小马哥Spring Boot 系列Java微服务实战视频教程
链接失效或更多好课请联系微信 ZA_summer 01.Java 微服务实践 – Spring Boot 系列(一)初体验 02.Java 微服务实践 – Spring Boot 系列(二) Web篇 ...
- java散列法的运用实例,Java HashMap compute() 使用方法及示例
Java HashMap compute() 使用方法及示例 Java HashMap compute()方法计算一个新值,并将其与哈希映射中的指定键相关联. compute()方法的语法为: has ...
- java实现登陆ssm框架_SSM框架搭建web服务器实现登录功能(Spring+SpringMVC+Mybatis)
初学java EE,虽然知道使用框架会使开发更加便捷高效,但是对于初学者来说,感到使用框架比较迷惑,尤其是各种jar包的引用.各种框架的配置.注解的使用等等. 最好的学习方法就是实践,于是下载了一个现 ...
- java 代码通用结构_java spring代码通用结构-java
src.main. java.com.company.projectname | - aop:类组.Spring AOP的Aspect仓库,是AOP的相关内容.定义了AOP切面类与织入方法.涉及@As ...
- java 管理系统登陆完毕后关闭窗口_【求助】登录窗口登录成功后隐藏窗口
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 代码如下: import java.awt.*; import java.awt.event.*; import java.sql.ResultSet; ...
- java 模拟登陆web系统_关于java模拟登陆WEB的问题。
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 import java.io.BufferedReader; import java.io.IOException; import java.io.Inp ...
最新文章
- 如何在html中写javascript代码,如何在本地html代码中使用javascript脚本
- 2018.8.17提高B组模拟考试
- 周周有好文2007-10-28 2007-11-3
- 【乱侃】How do they look them ?
- PHP保留小数的相关方法
- 操作主机 Infrastructure Master[为企业维护windows server 2008系列八]
- UNIX TCP回射服务器/客户端之使用epoll模型的服务器
- 使用CSS和JQuery,模拟超链接的用户单击事件
- 苹果iPod设计及商业操作内幕
- 软件工程毕业设计要求
- 前车之鉴:从被回绝的系列原因出发,解读应聘阿里的注意事项
- aircrack安装并破解wifi
- 网络编程、通信三要素、UDP快速入门、TCP通信、即时通信、模拟BS系统
- Android游戏添加游戏动画,Android游戏中的动画制作
- Vue2.0 —— 运用算法实现 AST 抽象语法树
- ps因计算机限制打不开,电脑突然坏了只有打不开PS这是什么原因在线 – 手机爱问...
- MySQL limit后面加变量
- [附源码]Python计算机毕业设计东北鹿产品售卖网站Django(程序+LW)
- python列表中的元素可以是不同类型_Python列表中所有元素必须为相同类型的数据。...
- Android零基础开发到项目实战