自定义资源认证规则
- /index公共资源
- /hello受保护资源
在项目中添加以下配置可以设置资源权限规则:
创建配置类,继承WebSecurityConfigurerAdapter,重写其中的configure(HttpSecurity http)该方法最终用于类别@Configuration.
@Configuration public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests() .mvcMatchers("/index") .permitAll() .anyRequest() .authenticated() .and() .formLogin(); } }
# 说明 - permitAll() 该资源是公共资源,无需认证和授权即可直接访问 - anyRequest().authenticated()代表所有请求,必须经过认证才能访问 - formLogin() 代表打开表单认证 ## 注:放行资源必须放在所有认证请求之前!
1.定制登录界面
@Configuration public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests() .mvcMatchers("/loginHtml").permitAll() //请求路径,即Controller放行自定义登录界面的路径 .mvcMatchers("/index").permitAll() //请求路径/index放行
.anyRequest().authenticated() //其他的任何请求都要进行认证
.and()
.formLogin() //表单验证
.loginPage("/loginHtml") //默认登录页面,也是通过请求进行跳转至默认登录页面
.loginProcessingUrl("/doLogin") //指定发送过来的/doLogin请求被捕获,进行权限验证。
.usernameParameter("uname") //默认接收username参数,修改为uname
.passwordParameter("passwd") //默认接收password参数,修改为passwd
// .successForwardUrl("/index") //认证成功 forward跳转路径 地址栏不变 每次都默认跳转到/index
.defaultSuccessUrl("/index",true) //认证成功之后的跳转 重定向
// 如果之前保存了请求但是被拦截,在拦截之后先跳转到被保存的请求,如果没有,则跳转到指定的请求。
// 如果第二个参数设置为true,则无论如何,只要认证成功,不管有没有保存的请求直接跳转到指定的请求。
.and()
.csrf().disable(); //禁止 csrf 跨域请求保护
}
}
-
登录表单method必须为post。
-
antion的请求路径与配置类中的loginProcessingUrl()一致。
-
用户名密码的参数也需要与配置类中的usernameParameter()、passwordParameter()一致。
-
successForwardUrl、defaultSuccessUrl这个两个方法都可以实现成功之后跳转
- successForwardUrl默认使用forward跳转 注意:不会跳转到之前请求路径
- defaultSuccessUrl默认使用redirect跳转 注意:如果之前请求路径,会优先跳转之前请求路径,可以传入第二个参数进行修改。
2.自定义登录成功处理
有时候页面跳转并不能满足我们,特别是。
public interface AuthenticationSuccessHandler {
/** * Called when a user has been successfully authenticated. * @param request the request which caused the successful authentication * @param response the response * @param authentication the <tt>Authentication</tt> object which was created during * the authentication process. */
void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException;
}
根据接口的描述信息,也可以得知登录成功会自动回调这个方法,进一步查看它的默认类型,发现successForwardUrl、defaultSuccessUrl也是由它的子类实现的。
- 自定义AuthenticationSuccessHandler实现
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String,Object> result = new HashMap<String,Object>();
result.put("msg","登录成功");
result.put("status",200);
response.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}
}
- 配置AuthenticationSuccessHandler
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
//...
.and()
.formLogin() //表单验证
//...
//自定义登录成功处理
.successHandler(new MyAuthenticationSuccessHandler())
.failureForwardUrl("/loginHtml")
.and()
.csrf().disable(); //禁止 csrf 跨域请求保护
}
}
3.显示登录失败信息
为了能够更直观在登录页面看到异常错误信息,可以在登录页面中直接获取异常信息。Spring Security在登录失败之后会将异常信息存储到request,session作用域中key为
源码可以参考:SimpleUrlAuthenticationFailureHandler
- 显示异常信息
- 配置
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
//...
.and()
.formLogin() //表单验证
//...
//.failureForwardUrl("/loginHtml") //认证失败之后的forward跳转 forward -->异常信息存放在request
.failureUrl("/loginHtml") // 默认 认证失败之后的 redirect 跳转 redirect -->异常信息存放在session
.and()
.csrf().disable(); //禁止 csrf 跨域请求保护
}
}
4.自定义登录失败处理
和自定义登录成功处理一样,Spring Security同样为前后端分离开发提供了登录失败的处理,这个类就是AuthenticationFailureHandler。
源码:
public interface AuthenticationFailureHandler {
/** * Called when an authentication attempt fails. * @param request the request during which the authentication attempt occurred. * @param response the response. * @param exception the exception which was thrown to reject the authentication * request. */
void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException;
}
根据接口的描述信息,也可以得知登录失败会自动回调这个方法,进一步查看它的默认实现,发现failureUrl、failureForwardUrl也是由它的子类实现的。
- 自定义AuthenticationFailureHandler实现
/* * 自定义登录失败解决方案 * */
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
Map<String,Object> result = new HashMap<>();
result.put("msg","登录失败:"+exception.getMessage());
result.put("status",500);
response.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}
}
配置AuthenticationFailureHandler
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
//...
.and()
.formLogin() //表单验证
//...
.successHandler(new MyAuthenticationSuccessHandler()) //用来自定义认证成功之后处理 前后端分离解决方案
// .failureForwardUrl("/loginHtml") //认证失败之后的forward跳转 forward -->异常信息存放在request
// .failureUrl("/loginHtml") // 默认 认证失败之后的 redirect 跳转 redirect -->异常信息存放在session
.failureHandler(new MyAuthenticationFailureHandler()) //用来自定义认证失败之后处理 前后端分离解决方案
.and()
.csrf().disable(); //禁止 csrf 跨域请求保护
}
}
5.注销登录
Spring Security中提供了默认的注销登录配置,在开发时也可以按照自己需求对注销进行个性化定制。
-
开启注销登录
默认开启 @Configuration public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeHttpRequests() //... .and() .formLogin() //表单验证 //... .and() .logout() //开启注销登录 默认 .logoutUrl("/logout") //指定注销登录 url 默认 默认请求方式:GET .invalidateHttpSession(true) //默认 会话失效 .clearAuthentication(true) //默认 清楚认证标记 .logoutSuccessUrl("/loginHtml") //注销登录成功之后跳转 .and() .csrf().disable(); //禁止 csrf 跨域请求保护 } }
- 通过logout()方法注销配置
- logoutUrl指定退出登录请求地址,默认是GET请求,路径为/logout
- invalidateHttpSession退出时是否是session失效,默认值为true
- clearAuthentication退出时是否清楚认证信息,默认值为true
- logoutSuccessUrl退出登录时跳转地址
-
-
配置多个注销登录请求
如果项目中有需要,开发者还可以配置多个注销登录的请求,同时还可以指定请求的方法:
@Configuration public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeHttpRequests() //... .and() .formLogin() //表单验证 //... .and() .logout() //开启注销登录 默认 .logoutRequestMatcher(new OrRequestMatcher( new AntPathRequestMatcher("/aa","GET"), new AntPathRequestMatcher("/bb","POST") )) // .invalidateHttpSession(true) //默认 会话失效 .clearAuthentication(true) //默认 清楚认证标记 .logoutSuccessUrl("/loginHtml") //注销登录成功之后跳转 .and() .csrf().disable(); //禁止 csrf 跨域请求保护 } }
-
前后端分离注销登录配置
如果是前后端分离开发,注销成功之后就不需要页面跳转了,只需要将注销成功的信息返回前端即可,此时我们可以通过自定义LogoutSuccessHandler实现来返回内容注销之后信息:
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String,Object> result = new HashMap<String,Object>();
result.put("msg","注销成功");
result.put("status",200);
response.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}
}
配置
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
//...
.and()
.formLogin() //表单验证
//...
.and()
.logout() //开启注销登录 默认
.logoutRequestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/aa","GET"),
new AntPathRequestMatcher("/bb","POST")
)) //
.invalidateHttpSession(true) //默认 会话失效
.clearAuthentication(true) //默认 清楚认证标记
.logoutSuccessHandler(new MyLogoutSuccessHandler()) //注销登录成功之后处理
.and()
.csrf().disable(); //禁止 csrf 跨域请求保护
}
}
6.登录用户数据获取
6.1.SecurityContextHolder
Spring Security会将登录用户数据保存在Session中。但是,为了使用方便,Spring Security在此基础上还做了一些改进,其中最主要的一个变化就是线程绑定。当用户登录成功后,Spring Security会将登录成功的用户信息保存到SecurityContextHolder中。
SecurityContextHolder中的数据保存默认是通过ThreadLocal来实现的,使用ThreadLocal创建的变量只能被当前线程访问,不能被其他线程访问和修改,也就是用户数据和请求线程绑定在一起。当登录请求处理完毕后,Spring Security会将SecurityContextHolder中的数据拿出来保存到Session中,同时将SecurityContextHolder中的数据清空。以后每当有请求到来时,Spring Security就会先从Session中取出用户登录数据,保存到SecurityContextHolder中,方便在该请求的后续处理过程中使用,同时在请求结束时将SecurityContextHolder中的数据拿出来保存到Session中,然后将SecurityContextHolder中的数据清空。
实际那个SecurityContextHolder中存储的是SecurityContext,在SecurityContext中存储是Authentication。
这种设计是典型的策略设计模式:
public class SecurityContextHolder {
public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
public static final String MODE_GLOBAL = "MODE_GLOBAL";
private static final String MODE_PRE_INITIALIZED = "MODE_PRE_INITIALIZED";
public static final String SYSTEM_PROPERTY = "spring.security.strategy";
private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
private static SecurityContextHolderStrategy strategy;
//...
private static void initializeStrategy() {
if (MODE_PRE_INITIALIZED.equals(strategyName)) {
Assert.state(strategy != null, "When using " + MODE_PRE_INITIALIZED
+ ", setContextHolderStrategy must be called with the fully constructed strategy");
return;
}
if (!StringUtils.hasText(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
return;
}
if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
return;
}
if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
return;
}
// Try to load a custom strategy
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
//...
}
MODE THREADLOCAL :这种存放策略是将SecurityContext存放在ThreadLocal中,Threadlocal的特点是在哪个线程中存储就要在哪个线程中读取,这其实非常适合web应用,因为在默认情况下,一个请求无论经过多少Filter到达Servlet,都是由一个线程来处理。这也是SecurityContextHolder的默认存储测录额,这种存储策略以为着如果在具体的业务处理代码中,开启了子线程,在子线程中去获取登录用户数据,就会获取不到MODE INHERITABLETHREADLOCAL :这种存储模式适用于多线程环境,如果希望在子线程中ue能够获取到登录用户数据,那么可以使用这种存储模式。MODE GLOBAL :这种存储模式实际上是将数据保存在一个静态变量中,在JavaWeb开发中,这种模式很少使用到。
6.2.SecurityContextHolderStrategy
通过SecurityContextHolder可以得知,SecurityContextHolderStrategy接口用来定义存储策略方法
public interface SecurityContextHolderStrategy {
void clearContext();
SecurityContext getContext();
void setContext(SecurityContext context);
SecurityContext createEmptyContext();
}
接口中一共定义了四个方法:
- clearContext:该方法用来清除存储的SecurityContext对象。
- getContext:该方法用来获取存储的SecurityContext对象。
- setContext:该方法用来设置存储的SecurityContext对象。
- create Empty Context:该方法则用来创建一个空的SecurityContext对象。
从上面可以看出每一个实现类对应一种策略的实现。
6.3.代码中获取认证之后用户数据
@RestController public class HelloController { 标签:
gn325blr固态继电器