SpringSecurity框架做为一款安全框架,能够拦截咱们的请求,默认状况下使用的是basic认证的方式,如图一那样。可是,这种方式很不友好,每次的密码都是动态的变化的,没法自定义,咱们能够在application.properties配置security.basic.enabled=false进行关闭。可是此时谁均可以访问咱们的接口,很不安全。此时能够在在跟包下建立一个BrowswerSecurityConfig类,集成WebSecurityConfiguerAdapter适配器,此时访问就是如图二那样 http.formLogin()//表示使用表单登陆 .and() .authorizeRequests()//表示对请求的受权 .anyRequest()//对任何请求受权 .authenticated();//都须要身份认证html
SpringSecurity其实是一系列的过滤器链前端
1.UsernamepasswordAuthenticationFilter主要负责表单的认证java
2.BasicAuthenticationFilter主要负责SpringSecurity默认的basic认证web
3.ExceptionTranslationFilter:根据抛出的错误跳转到对应的页面数据库
4.FilterSecurityInterceptor是过滤器类,根据咱们的配置是否能够访问后台的接口json
1.处理用户信息获取逻辑缓存
要获取用户的信息就须要实现UserDetailsService,而且实现UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;方法安全
2.处理用户校验逻辑 用户的校验可使用关联咱们的数据库,根据数据库的数据来进行校验,校验以后会返回一个UserDtails对象。这个对象类以下:服务器
public interface UserDetails extends Serializable { Collection<? extends GrantedAuthority> getAuthorities(); //用户名 String getPassword(); //密码 String getUsername(); //帐户是否超时 boolean isAccountNonExpired(); //帐户是否被冻结 boolean isAccountNonLocked(); //密码是否失效 boolean isCredentialsNonExpired(); //是否可用 boolean isEnabled(); }
3.处理密码加密解密 须要在BrowswerSecurityConfig.java配置passwordEncoder加密器app
/** * 配置密码编码器 * [@return](https://my.oschina.net/u/556800) */ [@Bean](https://my.oschina.net/bean) public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }
在MyUserDetailsService中的代码以下:
[@Component](https://my.oschina.net/u/3907912) public class MyUserDetailService implements UserDetailsService { @Autowired PasswordEncoder passwordEncoder; private Logger logger = LoggerFactory.getLogger(MyUserDetailService.class); [@Override](https://my.oschina.net/u/1162528) public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { logger.info("登陆用户名:"+s); String password = passwordEncoder.encode("123"); logger.info("用户密码:"+password); return new User(s,password/*"123"*/, true,true,true,true, AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); } }
1.自定义登陆页面 自定义登陆页面须要在代码中配置以下的代码:
http .formLogin()//表示使用表单登陆 .loginPage("/imooc-signIn.html")//表示条状登陆页面 .and() .authorizeRequests()//表示对请求的受权 .anyRequest()//对任何请求受权 .authenticated();//都须要身份认证
可是若是这样以访问会出问题,由于即便是访问登陆页面,也须要登陆认证,会出现屡次访问的状况:
此时代码还须要放权,添加的代码以下:
.antMatchers("/imooc-signIn.html") .permitAll()
加了这个放行的代码以后再次访问会出现以下的显示:
默认状况下的UsernamepasswordAthenticationFilter过滤的路径是/login,源码:
public UsernamePasswordAuthenticationFilter() { super(new AntPathRequestMatcher("/login", "POST")); }
为了让SpringSecurity可以过滤咱们自定义的路径,如咱们表单的路径/authentication/form,须要在BrowswerSecurityConfig.java内配置以下:
.loginProcessingUrl("/authentication/form")//表示让过滤器处理咱们自定义的路径的
此时在此登陆会发现以下状况(由于SpringSecurity提供了跨站访问伪造的一个防御):
此时能够想关闭这个防御
.and() .csrf().disable();//关闭跨站伪造的防御
处理不一样类型的请求
须要添加一个controller,代码以下所示:
@RestController public class BrowserSecurityController { private Logger logger = LoggerFactory.getLogger(BrowserSecurityController.class); //请求缓存 private RequestCache requestCache = new HttpSessionRequestCache(); //调转工具 private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); @Autowired SecurityProperties securityProperties; /** * 当须要省份认证时跳转到这里 * * @param request * @param response * @return */ @RequestMapping("/authentcation/require") @ResponseStatus(code = HttpStatus.UNAUTHORIZED) public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException { //拿到引起跳转的请求 SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest != null) { String targetUrl = savedRequest.getRedirectUrl(); logger.info("引起跳转的请求是:" + targetUrl); //若是请求的是html,那么就直接到登陆的html if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) { redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage()); } } return new SimpleResponse("访问的服务须要省份认证,请引导用户到登陆页面"); } }
除了添加上年的controller代码以外还须要在配置添加以下的代码:
http // .httpBasic()//使用的是SpringSecurity的基本认证 .formLogin()//表示使用表单登陆 .loginPage("/authentcation/require")//经过controller去处理不一样类型的请求 .loginProcessingUrl("/authentication/form")//表示让过滤器处理咱们自定义的路径的 .and() .authorizeRequests()//表示对请求的受权 //表示对登陆的页面容许访问,不须要受权 .antMatchers("/authentcation/require",securityProperties.getBrowser().getLoginPage()) .permitAll() .anyRequest()//对任何请求受权 .authenticated()//都须要身份认证 .and() .csrf().disable();//关闭跨站伪造的防御
2.自定义登陆成功处理 须要实现AuthenticationSuccessHandler接口
@Component("imoocAuthenticationSuccessHandler") public class ImoocAuthenticationSuccessHandler implements AuthenticationSuccessHandler { private Logger logger = LoggerFactory.getLogger(ImoocAuthenticationSuccessHandler.class); @Autowired private ObjectMapper objectMapper; /** * * @param request * @param response * @param authentication 包装了全部的关于用户的信息 * @throws IOException * @throws ServletException */ @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { logger.info("登陆成功"); response.setContentType("application/json;charset=UTF-8"); //将authentication以json字符串显示回去 response.getWriter().write(objectMapper.writeValueAsString(authentication)); } }
此时已经完成了自定义的的登陆成功的处理器的编写,可是仍然没法使用,须要注册进去
/** * 专门用来作web安全应用的适配器WebSecurityConfigurerAdapter */ @Configuration public class BrowswerSecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired SecurityProperties securityProperties; /** * 登陆成功处理器 */ @Autowired ImoocAuthenticationSuccessHandler imoocAuthenticationSuccessHandler; /** * 登陆失败处理器 */ @Autowired ImoocAuthenticationFailureHandler imoocAuthenticationFailureHandler; /** * 配置密码编码器 * @return */ @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http // .httpBasic()//使用的是SpringSecurity的基本认证 .formLogin()//表示使用表单登陆 .loginPage("/authentcation/require")//表示条状登陆页面 .loginProcessingUrl("/authentication/form")//表示让过滤器处理咱们自定义的路径的 .successHandler(imoocAuthenticationSuccessHandler)//注册咱们的登陆成功的处理器 .failureHandler(imoocAuthenticationFailureHandler)//注册咱们自定义的登陆失败处理器 .and() .authorizeRequests()//表示对请求的受权 //表示对登陆的页面容许访问,不须要受权 .antMatchers("/authentcation/require",securityProperties.getBrowser().getLoginPage()) .permitAll() .anyRequest()//对任何请求受权 .authenticated()//都须要身份认证 .and() .csrf().disable();//关闭跨站伪造的防御 } }
此时登陆访问,登陆成功会出现以下的表现:
3.自定义登陆失败处理
须要实现AuthenticationFailureHandler接口
@Component("imoocAuthenticationFailureHandler") public class ImoocAuthenticationFailureHandler implements AuthenticationFailureHandler { private Logger logger = LoggerFactory.getLogger(ImoocAuthenticationFailureHandler.class); @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { logger.info("登陆失败"); response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());//设置服务器错误 //将authentication以json字符串显示回去 response.getWriter().write(objectMapper.writeValueAsString(exception)); } }
完成了处理器的代码编写统一须要注册,登陆访问失败会有以下的返回值:
前端是表单提交的时候可能就须要直接跳转到登陆以前的路径,此时就须要使用跳转,不然就返回json合适
为了实现能够给用户配置,在配置类添加一个登陆的类型
public enum LoginType { REDIRECT,//跳转 JSON//返回json } public class BrowserProperties { private String loginPage = "/imooc-signIn.html"; private LoginType loginType = LoginType.JSON; public BrowserProperties() { } public String getLoginPage() { return loginPage; } public void setLoginPage(String loginPage) { this.loginPage = loginPage; } public LoginType getLoginType() { return loginType; } public void setLoginType(LoginType loginType) { this.loginType = loginType; } }
在登陆成功失败添加登陆类型判断:
if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())) { response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());//设置服务器错误 //将authentication以json字符串显示回去 response.getWriter().write(objectMapper.writeValueAsString(exception)); }else { }
在代码添加好这些功能以后就能够在配置文件配置:
imooc.security.browser.loginType=REDIRECT