1.认证(你是谁) 2.受权(能干吗) 3.攻击防御(防止伪造身份)html
其实Spring Security是一系列的过滤器链 java
在这条顾虑其链上,若是咱们定义的是表单认证,那么UsernamePasswordAuthenticationFilter就会起做用,若是是Basic认证,那么BasicAuthenticationFilter就起做用,这条链上若是不相关的是不会起做用的。最后到达FilterSecurityInterceptor来判断是否能够访问REST API,若是不符合就抛出异常,此时ExceptionTranslationFilter起做用。spring
用户的信息被封装在了org.springframework.security.core.userdetails.UserDetailsService, 若是须要从本身的数据源获取用户数据,那么就须要实现UserDetailsService类,并实现loadUserByUsername(String s)json
@Component//TODO 使之成为用户的bean public class MyDetailService implements UserDetailsService { Logger logger = LoggerFactory.getLogger(MyDetailService.class); [@Override](https://my.oschina.net/u/1162528) public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //TODO 根据用户名查找用户信息 logger.info("登录用户名:"+s); return new User(s,"123456", AuthorityUtils.createAuthorityList("admin")); } }
能够不使用SpringSecurity的User,能够自定义一个类实现UserDetail,实现它的下面的四个方法。 //帐户是否过时 boolean isAccountNonExpired(); //帐户是否被锁 boolean isAccountNonLocked(); //证书是否过时 boolean isCredentialsNonExpired(); //是否可用 boolean isEnabled();缓存
例子:session
@Component//TODO 使之成为用户的bean public class MyDetailService implements UserDetailsService { Logger logger = LoggerFactory.getLogger(MyDetailService.class); [@Override](https://my.oschina.net/u/1162528) public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //TODO 根据用户名查找用户信息 logger.info("登录用户名:"+s); return new User(s,"123456",true,true,true,false, AuthorityUtils.createAuthorityList("admin")); } }
须要配置一个加密器:app
@Configuration public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { //生成一个加密器 @Bean public PasswordEncoder getPasswordEncoder(){ return new BCryptPasswordEncoder(); } ....} @Component//TODO 使之成为用户的bean public class MyDetailService implements UserDetailsService { ** @Autowired PasswordEncoder passwordEncoder;** Logger logger = LoggerFactory.getLogger(MyDetailService.class); @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //TODO 根据用户名查找用户信息 logger.info("登录用户名:"+s); String encode = passwordEncoder.encode("123456"); logger.info("登录用户名:"+encode); return new User(s,encode,true,true,true,true, AuthorityUtils.createAuthorityList("admin")); } }
执行结果:ide
2018-11-28 21:23:10.046 INFO 7560 --- [nio-8088-exec-3] com.flexible.service.MyDetailService : 登录用户名:user 2018-11-28 21:23:10.133 INFO 7560 --- [nio-8088-exec-3] com.flexible.service.MyDetailService : 登录用户名:$2a$10$S7UuQCqoIEnfzJJGZOnJuuavzZFlohoXA4IpGDkmxmmV9H.01XUIS
http.formLogin().loginPage("/imooc-signIn.html")//这里能够写登陆的界面的url,可是在以前的配置已经默认的拦截因此的url,因此会出现循环的打开登陆的界面,致使死循环,为了解决这种状况就须要使用.antMatchers("/imooc-signIn.html").permitAll()//表示匹配到登陆的时候受权。此时在点击任何该服务的连接就会跳到登陆界面,例如:测试
http.formLogin() .loginPage("/imooc-signIn.html") .and() .authorizeRequests()//对后面的请求受权 .antMatchers("/imooc-signIn.html").permitAll()//表示匹配到登陆的时候受权。 .anyRequest()//任何请求 .authenticated();//都须要身份认证
1.须要请求跳转。flex
2.存储跳转前的请求到httpSessionRequest里面。
3.将请求从缓存取出。
4.判断请求跳转时页面跳转仍是其余的,若是页面就能够时直接跳转(可是要自定义跳转的登陆页面就须要须要实现登陆页面时能够定制化的)。
5.鉴定权限,若是不是就登陆页面的请求跳转就是权限不够提示401。
例子: SecurityProperties.java
@ConfigurationProperties(prefix = "flexible.security") public class SecurityProperties { private BrowserProperties browser = new BrowserProperties(); public BrowserProperties getBrowser() { return browser; } public void setBrowser(BrowserProperties browser) { this.browser = browser; } }
BrowserProperties.java
public class BrowserProperties { //配置默认的登陆页 private String loginPage="/imooc-signIn.html"; public BrowserProperties() { } public String getLoginPage() { return loginPage; } public void setLoginPage(String loginPage) { this.loginPage = loginPage; } }
SecurityCoreConfig.java(让配置生)
/** * 让咱们配置的security配饰生效 */ @Configuration @EnableConfigurationProperties(SecurityProperties.class) public class SecurityCoreConfig { }
BrowserSecurityConfig.java(配置放行路径跳转到本身的路径)
http.formLogin() .loginPage("/authentication/require")//自定义的认证路径 .loginProcessingUrl("/authentication/form")//告诉springsecurity使用UsernamePasswordAuthenticationFilter来处理登陆 .and() .authorizeRequests()//对后面的请求受权,将自定义的登陆页面页放权 .antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage()).permitAll()//表示匹配到登陆的时候受权。 .anyRequest()//任何请求 .authenticated()//都须要身份认证 .and().csrf().disable();//将spring security为了防止CSRF攻击禁用
BrowserSecurityController.java
@RestController public class BrowserSecurityController { Logger logger = LoggerFactory.getLogger(BrowserSecurityController.class); @Autowired SecurityProperties securityProperties;//获取到自定义的登陆页 //拿到引起跳转的请求须要经过HttpSessionRequestCache来拿 private RequestCache requestCache = new HttpSessionRequestCache();//在跳转以前,springsecurity将请求缓存在这个httpSessionRequestCache里面 //实现的跳转的一个类 private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); /** * 须要身份认证会跳转到这里 * @param request * @param response * @return */ @RequestMapping("authentication/require") @ResponseStatus(code = HttpStatus.UNAUTHORIZED)//不是一个html就返回一个401的状态码 public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException { //拿到在跳转以前缓存的请求 SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest != null) { String target = savedRequest.getRedirectUrl(); logger.info("引起跳转的请求是:{}", target); if (StringUtils.endsWithIgnoreCase(target, ".html")) { //跳转,须要使用到RedirectStrategy redirectStrategy.sendRedirect(request,response,securityProperties.getBrowser().getLoginPage());//这里须要实现一个统一,通用的跳转,不单单跳到本身的界面。 } } return new SimpleResponse("须要用户认证,引导到登陆页"); } }
demo测试例子,在application.properties里面配置一个flexible.security.browser.loginPage="xxx.html"就能够了。
注释掉配置的flexible.security.browser.loginPage="xxx.html"就会跳转默认的登陆页。
须要实现AuthenticationSuccessHandler接口
FlexibleAuthenticationHandler.java
@Component(value = "flexibleAuthenticationHandler") public class FlexibleAuthenticationHandler implements AuthenticationSuccessHandler { Logger logger = LoggerFactory.getLogger(FlexibleAuthenticationHandler.class); @Autowired ObjectMapper objectMapper; /** * * @param httpServletRequest * @param httpServletResponse * @param authentication 封装了认证请求的信息(ip,session,用户信息) * @throws IOException * @throws ServletException */ @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { logger.info("....login success..."); httpServletResponse.setContentType("application/json;charset=UTF-8"); //将authentication对象一json格式返回去 httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication)); } }
须要告诉SpringSecurity成功后的处理
.successHandler(flexibleAuthenticationHandler)//告诉SpringSecurity成功以后使用的处理器
须要实现AuthenticationFailureHandler接口
FlexibleAuthenticationFailureHandler.java
@Component(value = "flexibleAuthenticationFailureHandler") public class FlexibleAuthenticationFailureHandler implements AuthenticationFailureHandler { Logger logger = LoggerFactory.getLogger(FlexibleAuthenticationFailureHandler.class); @Autowired ObjectMapper objectMapper; @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException authentication) throws IOException, ServletException { logger.info("....login failure..."); httpServletResponse.setContentType("application/json;charset=UTF-8"); httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); //将authentication对象一json格式返回去 httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication)); } }
告诉SpringSecurity失败后的处理器:
.failureHandler(flexibleAuthenticationFailureHandler)//告诉SpringSecurity失败后的处理器