本文源码请看这里

首先添加起步依赖(如果不是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登录,自定义验证相关推荐

  1. spring 加载java类_在Spring中基于Java类进行配置的完整步骤

    在Spring中基于Java类进行配置的完整步骤 发布于 2020-7-7| 复制链接 基于Java配置选项,可以编写大多数的Spring不用配置XML,下面 前言JavaConfig 原来是 Spr ...

  2. Java EE 6 VS Spring 3:Java EE杀死了Spring? 没门!

    介绍 几天前,我在听Java Spotlight Podcast的第85集 . 在这次演讲中, Bert Ertman和Paul Bakker讨论了从Spring迁移到Java EE的问题. 基本上, ...

  3. Java EE 6 VS Spring 3:Java EE已经杀死了Spring? 没门!

    介绍 几天前,我在听Java Spotlight Podcast的插曲85 . 在这次演讲中, Bert Ertman和Paul Bakker讨论了从Spring迁移到Java EE. 基本上,在他们 ...

  4. java小马哥springboot_小马哥Spring Boot 系列Java微服务实战视频教程

    链接失效或更多好课请联系微信 ZA_summer 01.Java 微服务实践 – Spring Boot 系列(一)初体验 02.Java 微服务实践 – Spring Boot 系列(二) Web篇 ...

  5. java散列法的运用实例,Java HashMap compute() 使用方法及示例

    Java HashMap compute() 使用方法及示例 Java HashMap compute()方法计算一个新值,并将其与哈希映射中的指定键相关联. compute()方法的语法为: has ...

  6. java实现登陆ssm框架_SSM框架搭建web服务器实现登录功能(Spring+SpringMVC+Mybatis)

    初学java EE,虽然知道使用框架会使开发更加便捷高效,但是对于初学者来说,感到使用框架比较迷惑,尤其是各种jar包的引用.各种框架的配置.注解的使用等等. 最好的学习方法就是实践,于是下载了一个现 ...

  7. java 代码通用结构_java spring代码通用结构-java

    src.main. java.com.company.projectname | - aop:类组.Spring AOP的Aspect仓库,是AOP的相关内容.定义了AOP切面类与织入方法.涉及@As ...

  8. java 管理系统登陆完毕后关闭窗口_【求助】登录窗口登录成功后隐藏窗口

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 代码如下: import java.awt.*; import java.awt.event.*; import java.sql.ResultSet; ...

  9. java 模拟登陆web系统_关于java模拟登陆WEB的问题。

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 import java.io.BufferedReader; import java.io.IOException; import java.io.Inp ...

最新文章

  1. 如何在html中写javascript代码,如何在本地html代码中使用javascript脚本
  2. 2018.8.17提高B组模拟考试
  3. 周周有好文2007-10-28 2007-11-3
  4. 【乱侃】How do they look them ?
  5. PHP保留小数的相关方法
  6. 操作主机 Infrastructure Master[为企业维护windows server 2008系列八]
  7. UNIX TCP回射服务器/客户端之使用epoll模型的服务器
  8. 使用CSS和JQuery,模拟超链接的用户单击事件
  9. 苹果iPod设计及商业操作内幕
  10. 软件工程毕业设计要求
  11. 前车之鉴:从被回绝的系列原因出发,解读应聘阿里的注意事项
  12. aircrack安装并破解wifi
  13. 网络编程、通信三要素、UDP快速入门、TCP通信、即时通信、模拟BS系统
  14. Android游戏添加游戏动画,Android游戏中的动画制作
  15. Vue2.0 —— 运用算法实现 AST 抽象语法树
  16. ps因计算机限制打不开,电脑突然坏了只有打不开PS这是什么原因在线 – 手机爱问...
  17. MySQL limit后面加变量
  18. [附源码]Python计算机毕业设计东北鹿产品售卖网站Django(程序+LW)
  19. python列表中的元素可以是不同类型_Python列表中所有元素必须为相同类型的数据。...
  20. Android零基础开发到项目实战

热门文章

  1. sleep 与 wait 区别
  2. 可变大小区(Variable-Size Extents)
  3. Oracle Golden Gate 系列十一 -- 配置 GG DDL 同步 说明 与 示例
  4. ORACLE的analyze及生成方式
  5. 使用yum查看安装了哪些软件包、某软件包是否已经安装
  6. 微软给程序代码加的css效果
  7. centos7下安全访问远程服务器
  8. python基础: String类型
  9. webbrowser载入地图网页出现脚本错误解决
  10. Android开发-下载网络图片并显示到本地