文章目录
为了提高项目组生产效率和适应市场人才技术线,项目其中一个模块决定摒弃公司传统的Spring+Struts框架(技术老旧,工程体量庞大),采用SpringBoot新搭建一个工程。登录认证、权限管理这些系统基础功能自然少不了。项目还达不到微服务架构的地步,SpringCloudAuth2的方案太重,先只打算用Spring Security。本文章记录一下搜寻的各有关Spring Security的文章,不断的更新中。。。
参考文章
SpringBoot集成Spring Security
总共10篇文章;
从简单例子到原理
Spring Security原理篇(三) HttpSecurity https://www.jianshu.com/p/6f1b129442a1
1.初始化HttpSecurity对象
1.2.3 HttpSecurity的部分方法 2 从配置到Filter
2.1 formLogin()的原理
2.1.1 formLogin配置的例子
2.2 直接添加过滤器
Spring Security 参考手册https://www.springcloud.cc/spring-security-zhcn.html#true-
Spring Security 提供大量的示例应用 lots of sample applications 使用-jc参数演示使用java配置Spring Security。
https://github.com/spring-projects
官网推荐的例子等
Spring Security 实战干货 系列文章
https://www.felord.cn/categories/spring-security/
这个系列的文章比较全,理论为主
注意:需要加博主微信获取验证码
Spring Security【系列文章】
4篇文章简单入门,适用大部分使用场景
-
Spring Security项目构建(一)
https://www.jianshu.com/p/bc86852190f9 -
Spring Security项目Spring MVC开发RESTful
API(二)https://www.jianshu.com/p/0646733976a5 -
Spring
Security项目基于表单登陆(三)https://www.jianshu.com/p/cbe37e91543a -
Spring Security项目第三方登陆(四)
https://www.jianshu.com/p/42f016a68831
集成QQ登录和微信登录简单逻辑。代码不全,先收藏借鉴。
源码位置补充:https://gitee.com/guohtime/springboot-security-social
采用springboot-security-social实现第三方认证
@Override
protected void configure(HttpSecurity http) throws Exception {
formAuthenticationConfig.configure(http);
http
.csrf().disable()//关闭跨站防护
.apply(validateCodeSecurityConfig) //校验验证码
.and()
.apply(smsCodeAuthenticationSecurityConfig)//手机验证码登陆
.and()
.apply(sociaSecurityConfig) //第三方登陆
.and()
.authorizeRequests()//下面授权配置
.antMatchers(
SecurityConstants.DEFAULT_UNAUTHENTICATION_URL, //处理登陆请求
SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_MOBILE, //手机登陆
securityProperties.getBrowser().getLoginPage(), //登陆页面
securityProperties.getBrowser().getSignUpUrl(), //注册页面
SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*", //验证码
"/user/regist")//第三方注册跟绑定
.permitAll()//login请求除外不需要认证
.anyRequest()
.authenticated()//所有请求都需要身份认证
.and()
.rememberMe() //记住密码
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds()) //失效时间
.userDetailsService(userDetailsService)
.and()
.sessionManagement()
.invalidSessionStrategy(invalidSessionStrategy) //session失效后的处理
.maximumSessions(securityProperties.getBrowser().getSession().getMaximumSessions()) //用户最大登陆数
.maxSessionsPreventsLogin(securityProperties.getBrowser().getSession().isMaxSessionsPreventsLogin())//是否阻止登陆
.expiredUrl(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)//用户只能登陆一次
.expiredSessionStrategy(sessionInformationExpiredStrategy)//用户被挤掉后的处理
.sessionRegistry(sessionRegistry)
.and()
.and()
.logout()
.logoutUrl("/sigOut")//默认退出路径是logOut可以自定义
.logoutSuccessHandler(logoutSuccessHandler)//处理退出到类
//.logoutSuccessUrl("/login")//退出后跳到到页面
.deleteCookies("JSESSIONID");
}
源码或者原理说明
认证流程
关键类
- UsernamePasswordAuthenticationFilter。用户名和密码认证过滤器
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter ;
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);
}
}
UsernamePasswordAuthenticationToken
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 520L;
private final Object principal;
private Object credentials;
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
public Object getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.principal;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
} else {
super.setAuthenticated(false);
}
}
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}
源码过程分析直接参考Spring boot + Spring Security 多种登录认证方式配置(一)https://blog.csdn.net/qq_36521507/article/details/103365805
不再重复贴源代码了。
Spring Security多种登录方式集成
要实现一个登录认证,首先要自定义一个过滤器 AbstractAuthenticationProcessingFilter,注入一个认证管理器 AuthenticationManager,然后需要绑定一个AbstractAuthenticationToken,注册一个认证处理器 AuthenticationProvider,如果使用默认认证过滤器,则只需要自定义认证处理器进行认证即可.
参考文章: Spring boot + Spring Security
多种登录认证方式配置(一)
https://blog.csdn.net/qq_36521507/article/details/103365805 Spring
boot + Spring Security
多种登录认证方式配置(二)https://blog.csdn.net/qq_36521507/article/details/103370070
应用场景:对接已有的认证系统特别是个性化的第三方认证系统。如果是LDAP、AUTH2等默认有集成,例子比较多。
Security + Vue 实现前后端分离登录
SpringBoot系列(六)SpringBoot 集成 Security + Vue 实现前后端分离登录https://blog.csdn.net/baidu_37832943/article/details/101064508
贴近项目实际情况的一个例子
有源代码和例子。 页面登录+验证码验证功能。
实现验证码校验
案例:实现验证码校验
1.使用过滤器实现验证码校验
总结
【参考Spring实战】
编写简单的安全性设置
SpringSecurity必须配置在一个实现了WebSecurityConfigurer的bean中,或者扩展WebSecurityConfigurerAdapter(简单方式)。
编写一个子类实现自定义配置或者实现。
这里的配置是关键
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter;
重载configure()方法
方法 | 描述 |
---|---|
configure(WebSecurity) | 配置Filter链 |
configure(HttpSecurity) | 配置拦截器保护请求 |
configure(AuthenticationManagerBuilder) | 配置user-detail服务 |
public static class SmsLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.apply(smsCodeAuthenticationSecurityConfig) //短信登陆配置
.and()
.authorizeRequests()
.antMatchers("/code/*").permitAll()
.antMatchers("/api/*").permitAll()
.anyRequest().authenticated()
.and()
//这里配置的为当未登录访问受保护资源时,返回json
.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPointHandler());
;
}
}
.apply(),可以配置另外一个WebSecurityConfigurerAdapter 子类;
.and() .authorizeRequests().antMatchers("/code/*").permitAll()使用Ant风格配置方式配置/code/请求路径的不认证也能访问,也就是不拦截请求。
.anyRequest().authenticated()代表后续请求需要认证后才能访问。
配置自定义用户服务(认证逻辑)
实现UserDetailsService 和UserDetails 接口,用于获取用户和角色信息。
这里就比较灵活了,不管是采用jdbc、redis、ldap或者通过resttemplate从第三方接口获取数据都是可以的。
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
//角色赋值例子
private List<String> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if (roles == null || roles.isEmpty()) {
return new ArrayList<>();
}
List<GrantedAuthority> authorities = new ArrayList<>(roles.size());
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
实现后的UserDetailsService 配置在configure(AuthenticationManagerBuilder) 下,例如:
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserDetailServiceImpl());
}