SpringBoot整合SpringSecurity,SESSION 并发管理,同账号只允许登录一次
重写了UsernamePasswordAuthenticationFilter,里面继承AbstractAuthenticationProcessingFilter,这个类里面的session认证策略,是一个空方法,貌似RememberMe也是.
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {
protected ApplicationEventPublisher eventPublisher;
protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private AuthenticationManager authenticationManager;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private RememberMeServices rememberMeServices = new NullRememberMeServices();
private RequestMatcher requiresAuthenticationRequestMatcher;
private boolean continueChainBeforeSuccessfulAuthentication = false;
private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
private boolean allowSessionCreation = true;
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
NullAuthenticatedSessionStrategy 源码
public final class NullAuthenticatedSessionStrategy implements SessionAuthenticationStrategy {
public NullAuthenticatedSessionStrategy() {
}
public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
}
}
所以必须自己配置一个session验证策略,以及配置并发控制.红字为关键
WebSecurityConfigurerAdapter
/**
* Created by ZhenWeiLai on on 2016-10-16.
* <p>
* 三种方法级权限控制
* <p>
* 1.securedEnabled: Spring Security’s native annotation
* 2.jsr250Enabled: standards-based and allow simple role-based constraints
* 3.prePostEnabled: expression-based
*/
@EnableWebSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Resource
private UserDetailsService userDetailsService; @Resource
private FilterInvocationSecurityMetadataSource securityMetadataSource; @Resource
private SessionRegistry sessionRegistry; @Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
web.ignoring().antMatchers("/components/**");
web.ignoring().antMatchers("/css/**");
web.ignoring().antMatchers("/images/**");
web.ignoring().antMatchers("/js/**");
web.ignoring().antMatchers("/mustache/**");
web.ignoring().antMatchers("/favicon.ico"); //注册地址不拦截
// web.ignoring().antMatchers("/base/invoice/userinfo/u/reg"); // web.ignoring().antMatchers("/**");
} @Override
protected void configure(HttpSecurity http) throws Exception {
//解决不允许显示在iframe的问题
http.headers().frameOptions().disable(); http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
//session并发控制过滤器
http.addFilterAt(new ConcurrentSessionFilter(sessionRegistry,sessionInformationExpiredStrategy()),ConcurrentSessionFilter.class);
//自定义过滤器
//在适当的地方加入
http.addFilterAt(filterSecurityInterceptor(securityMetadataSource, accessDecisionManager(), authenticationManagerBean()), FilterSecurityInterceptor.class); http.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")).and().logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll().and().exceptionHandling().accessDeniedPage("/accessDenied"); http.authorizeRequests().anyRequest().fullyAuthenticated(); // 关闭csrf
http.csrf().disable(); /**
* 以下配置无效
*/
//session管理
//session失效后跳转
// http.sessionManagement().invalidSessionUrl("/login");
// //只允许一个用户登录,如果同一个账户两次登录,那么第一个账户将被踢下线,跳转到登录页面
// http.sessionManagement().maximumSessions(1).expiredUrl("/login");
} @Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
// 自定义UserDetailsService,设置加密算法
auth.userDetailsService(userDetailsService);
//.passwordEncoder(passwordEncoder())
//不删除凭据,以便记住用户
auth.eraseCredentials(false);
} //session失效跳转
private SessionInformationExpiredStrategy sessionInformationExpiredStrategy() {
return new SimpleRedirectSessionInformationExpiredStrategy("/login");
} @Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
} //SpringSecurity内置的session监听器
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
} private UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new CuzUsernamePasswordAuthenticationFilter();
usernamePasswordAuthenticationFilter.setPostOnly(true);
usernamePasswordAuthenticationFilter.setAuthenticationManager(this.authenticationManager());
usernamePasswordAuthenticationFilter.setUsernameParameter("name_key");
usernamePasswordAuthenticationFilter.setPasswordParameter("pwd_key");
usernamePasswordAuthenticationFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/checkLogin", "POST"));
usernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler());
usernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
//session并发控制,因为默认的并发控制方法是空方法.这里必须自己配置一个
usernamePasswordAuthenticationFilter.setSessionAuthenticationStrategy(new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry));
return usernamePasswordAuthenticationFilter;
} // @Bean
// public LoggerListener loggerListener() {
// System.out.println("org.springframework.security.authentication.event.LoggerListener");
// return new LoggerListener();
// }
//
// @Bean
// public org.springframework.security.access.event.LoggerListener eventLoggerListener() {
// System.out.println("org.springframework.security.access.event.LoggerListener");
// return new org.springframework.security.access.event.LoggerListener();
// } /**
* 投票器
*/
private AbstractAccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList();
decisionVoters.add(new AuthenticatedVoter());
decisionVoters.add(new RoleVoter());//角色投票器,默认前缀为ROLE_
RoleVoter AuthVoter = new RoleVoter();
AuthVoter.setRolePrefix("AUTH_");//特殊权限投票器,修改前缀为AUTH_
decisionVoters.add(AuthVoter);
AbstractAccessDecisionManager accessDecisionManager = new AffirmativeBased(decisionVoters);
return accessDecisionManager;
} @Override
public AuthenticationManager authenticationManagerBean() {
AuthenticationManager authenticationManager = null;
try {
authenticationManager = super.authenticationManagerBean();
} catch (Exception e) {
e.printStackTrace();
}
return authenticationManager;
} /**
* 验证异常处理器
*
* @return
*/
private SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() {
return new SimpleUrlAuthenticationFailureHandler("/getLoginError");
} // /**
// * 表达式控制器
// *
// * @return
// */
// private DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() {
// DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
// return webSecurityExpressionHandler;
// } // /**
// * 表达式投票器
// *
// * @return
// */
// private WebExpressionVoter webExpressionVoter() {
// WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
// webExpressionVoter.setExpressionHandler(webSecurityExpressionHandler());
// return webExpressionVoter;
// } // Code5 官方推荐加密算法
// @Bean("passwordEncoder")
// public BCryptPasswordEncoder passwordEncoder() {
// BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// return bCryptPasswordEncoder;
// } // // Code3---------------------------------------------- /**
* 登录成功后跳转
* 如果需要根据不同的角色做不同的跳转处理,那么继承AuthenticationSuccessHandler重写方法
*
* @return
*/
private SimpleUrlAuthenticationSuccessHandler authenticationSuccessHandler() {
return new SimpleUrlAuthenticationSuccessHandler("/loginSuccess");
} /**
* Created by ZhenWeiLai on on 2016-10-16.
*/ private FilterSecurityInterceptor filterSecurityInterceptor(FilterInvocationSecurityMetadataSource securityMetadataSource, AccessDecisionManager accessDecisionManager, AuthenticationManager authenticationManager) {
FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
filterSecurityInterceptor.setSecurityMetadataSource(securityMetadataSource);
filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager);
filterSecurityInterceptor.setAuthenticationManager(authenticationManager);
return filterSecurityInterceptor;
} }
继承UsernamePasswordAuthenticationFilter ,注入SessionRegistry ,当用户登录验证成功后注册session
/**
* Created by ZhenWeiLai on 2017/6/2.
*/
public class CuzUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private boolean postOnly = true; @Resource
private SessionRegistry sessionRegistry; @Override
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);
//用户名密码验证通过后,注册session
sessionRegistry.registerNewSession(request.getSession().getId(),authRequest.getPrincipal());
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
}
重写用户实体类的比较方法.
/**
* 重写比较方法,SpringSecurity根据用户名来比较是否同一个用户
*/
@Override
public boolean equals(Object o){
if(o.toString().equals(this.username))
return true;
return false;
} @Override
public int hashCode(){
return username.hashCode();
} @Override
public String toString() {
return this.username;
}
SpringBoot整合SpringSecurity,SESSION 并发管理,同账号只允许登录一次的更多相关文章
- SpringBoot整合SpringSecurity示例实现前后分离权限注解
SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证 作者:Sans_ juejin.im/post/5da82f066fb9a04e2a73daec 一.说 ...
- SpringBoot整合SpringSecurity简单实现登入登出从零搭建
技术栈 : SpringBoot + SpringSecurity + jpa + freemark ,完整项目地址 : https://github.com/EalenXie/spring-secu ...
- 9、SpringBoot整合之SpringBoot整合SpringSecurity
SpringBoot整合SpringSecurity 一.创建项目,选择依赖 选择Spring Web.Thymeleaf即可 二.在pom文件中导入相关依赖 <!-- 导入SpringSecu ...
- boke练习: springboot整合springSecurity出现的问题,传递csrf
boke练习: springboot整合springSecurity出现的问题,传递csrf freemarker模板 在html页面中加入: <input name="_csrf&q ...
- springboot整合springsecurity遇到的问题
在整合springsecurity时遇到好几个问题,自动配置登录,下线,注销用户的操作,数据基于mybatis,模版引擎用的thymeleaf+bootstrap. 一.认证时密码的加密(passwo ...
- SpringBoot整合SpringSecurity实现JWT认证
目录 前言 目录 1.创建SpringBoot工程 2.导入SpringSecurity与JWT的相关依赖 3.定义SpringSecurity需要的基础处理类 4. 构建JWT token工具类 5 ...
- boke练习: springboot整合springSecurity出现的问题,post,delete,put无法使用
springboot 与 SpringSecurity整合后,为了防御csrf攻击,只有GET|OPTIONS|HEAD|TRACE|CONNECTION可以通过. 其他方法请求时,需要有token ...
- SpringBoot整合Mybatis完整详细版二:注册、登录、拦截器配置
接着上个章节来,上章节搭建好框架,并且测试也在页面取到数据.接下来实现web端,实现前后端交互,在前台进行注册登录以及后端拦截器配置.实现简单的未登录拦截跳转到登录页面 上一节传送门:SpringBo ...
- SpringBoot 整合 SpringSecurity 梳理
文档 Spring Security Reference SpringBoot+SpringSecurity+jwt整合及初体验 JSON Web Token 入门教程 - 阮一峰 JWT 官网 Sp ...
随机推荐
- Python初识 - day5
一.装饰器(decorator) 1.定义:本质是函数(装饰其它函数),就是为了其它函数添加附加功能. 2.原则:一是不能修改被装饰函数的源代码:二是不能修改被装饰函数的调用方式. 3.装饰器包含的知 ...
- form表单action=""的作用
看项目时发现action="",可仍旧提交到后台相关页面了.查了一下,action=""相当于当前页面刷新,不过页面按照form表单提交参数到后台@参考文章
- junit源码解析--测试驱动运行阶段
前面的博客里面我们已经整理了junit的初始化阶段,接下来就是junit的测试驱动运行阶段,也就是运行所有的testXXX方法.OK,现在我们开始吧. 前面初始化junit之后,开始执行doRun方法 ...
- tcpdump 使用
例子: 首先切换到root用户 tcpdump -w aaa.cap -i eth7 -nn -x 'port 9999' -c 1 以例子说明参数: -w:输出到文件aaa.cap ...
- lodash源码分析之baseFindIndex中的运算符优先级
我悟出权力本来就是不讲理的--蟑螂就是海米:也悟出要造反,内心必须强大到足以承受任何后果才行. --北岛<城门开> 本文为读 lodash 源码的第十篇,后续文章会更新到这个仓库中,欢迎 ...
- 6 个 Linux 运维典型问题
作为一名合格的 Linux 运维工程师,一定要有一套清晰.明确的解决故障思路,当问题出现时,才能迅速定位.解决问题,这里给出一个处理问题的一般思路: 重视报错提示信息:每个错误的出现,都是给出错误提示 ...
- iozone测试磁盘性能
什么是iozone? iozone是一个文件系统的benchmark工具,可以测试不同的操作系统中文件系统的读写性能. 可以测试 Read, write, re-read,re-write, read ...
- form表单中get和post两种提交方式的区别
一.form表单中get和post两种提交方式的区别? 1.get提交表单中的内容在链接处是可见的.post不可见 2.post相比于get是安全的 3.post不收限制大小,get有限制大小(黑马视 ...
- dubbo中Listener的实现
这里继续dubbo的源码旅程,在过程中学习它的设计和技巧,看优秀的代码,我想对我们日程编码必然有帮助的.而那些开源的代码正是千锤百炼的东西,希望和各位共勉. 拿ProtocolListenerWrap ...
- 51Nod 欢乐手速场1 A Pinball[DP 线段树]
Pinball xfause (命题人) 基准时间限制:1 秒 空间限制:262144 KB 分值: 20 Pinball的游戏界面由m+2行.n列组成.第一行在顶端.一个球会从第一行的某一列出发 ...