1.定义本身的登陆页面
咱们须要根据本身的业务系统构建本身的登陆页面以及登陆成功、失败处理
在spring security提供给个人登陆页面中,只有用户名、密码框,而自带的登陆成功页面是空白页面(能够重定向以前请求的路径中),而登陆失败时也只是提示用户被锁定、过时等信息。html
在实际的开发中,则须要更精细力度的登陆控制,记录错误的日志(错误的次数等)spring
2.自定义登陆页面json
http.formLogin() .loginPage("/sign.html") //位于resources/resources/sign.html .and() .authorizeRequests() .anyRequest().authenticated();
页面内容以下:浏览器
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>sign.html</title> </head> <body> <h2>标准登陆页面</h2> <h3>表单登陆</h3> <form action="/authentication/form" method="post"> <table> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"></td> </tr> <tr> <td colspan="2"><button type="submit">登陆</button></td> </tr> </table> </form> </body> </html>
启动以后,访问咱们的路径:http://localhost:8080/sign.html
这个时候浏览器会报错:重定向的次数过多,这是为何呢?
由于在配置中,咱们只是配置了loginPage("/sign.html"),可是又没有给该请求作受权,又由于没有作受权,又被跳转给sign.html页面,因此会是:重定向的次数过多。
此时:须要再配置受权路径,改造以后缓存
http.formLogin() .loginPage("/sign.html") .and() .authorizeRequests() .antMatchers("/sign.html").permitAll() //当访问sign.html这个页面的时候不须要进行身份认证 .anyRequest().authenticated();
在sign.html中,本身配置了一个post路径,在spring security原理中,表单登陆其实是由UsernamePasswordAuthenticationFilter这个过滤器来处理的,在这个过滤器中
处理的是/login 请求,为了让UsernamePasswordAuthenticationFilter这个过滤器知道处理咱们自定义的登陆路径/authentication/form,还须要再配置登陆的处理请求安全
http.formLogin() .loginPage("/sign.html") .loginProcessingUrl("/authentication/form") //登陆请求 .and() .authorizeRequests() .antMatchers("/sign.html").permitAll() .anyRequest().authenticated();
启动以后,继续访问咱们的路径:http://localhost:8080/sign.html
输入帐户名和密码,登陆以后会报错,403app
在默认状况下,spring security提供了跨站请求伪造的防御,用CSRF Token来完成的,在***和防御的时候再细讲,目前先把跨站请求伪造的功能先disable掉。ide
http.formLogin() .loginPage("/sign.html") .loginProcessingUrl("/authentication/form") //登陆请求 .and() .authorizeRequests() .antMatchers("/sign.html").permitAll() .anyRequest().authenticated() .and() .csrf().disable();
启动以后,继续访问咱们的路径:http://localhost:8080/user/1 ,系统会帮咱们重定向到sign.html页面
此时咱们输入正确的用户名和密码就能够访问咱们的请求了post
3.优化rest请求和html请求
完成了基本功能以后还须要继续优化咱们的代码结构,是要面向可重用的一种。
目前有2个问题:优化
当咱们接收到html请求或者数据请求的时候,先判断是否须要身份认证(spring security来作的)若是是否的话就直接返回了,若是是的话则须要跳转到咱们自定义的Controller上面去(目前咱们的作法是跳转到了sign.html页面上)在该方法内判断是html请求仍是数据请求。
Controller
@RestController public class BrowserSecurityController { private Logger logger = LoggerFactory.getLogger(BrowserSecurityConfig.class); //拿到引起跳转的请求(HttpSessionRequestCache把当前的请求缓存到Session中) private RequestCache requestCache = new HttpSessionRequestCache(); private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); @Autowired private SecurityProperties securityProperties; //当须要身份认证时,跳转到这里 @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 targetUrl = savedRequest.getRedirectUrl(); logger.info("引起跳转的请求是:"+targetUrl); //是否以.html结尾,若是是则跳转到登陆页面 if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) { //这个url,咱们须要作成可配置的url(由于咱们不可能每次都跳转到咱们本身的写的固定登陆页面,须要根据每一个项目的不一样) //这个时候就须要用到**Properties 配置文件类来作灵活性配置 redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage()); } //若是不是html请求,则返回401状态码以及错误信息 } return new SimpleResponse("访问的服务须要身份认证,请引导用户到登陆页"); } }
SecurityProperties自定义的登陆页配置属性类,为了可配置化
#另外的项目的登陆请求的页面 core.security.browser.loginPage = /demo-sign.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>demo-sign</title> </head> <body> <h2>demo-sign自定义登陆页</h2> </body> </html>
而咱们不想单纯的要一个简单的配置是,而是可管理的配置类,由于后面会有其余的配置,例如验证码的配置,OAuth的配置,等 这个时候须要同一个的配置类入口(SecurityProperties)
//读取配置文件内的信息 @ConfigurationProperties(prefix="core.security") public class SecurityProperties { private BrowserProperties browser = new BrowserProperties(); public BrowserProperties getBrowser() { return browser; } public void setBrowser(BrowserProperties browser) { this.browser = browser; } } public class BrowserProperties { //标准的登陆页面,若是其余项目没有配置则使用默认的登陆配置 private String loginPage = "/sign.html"; public String getLoginPage() { return loginPage; } public void setLoginPage(String loginPage) { this.loginPage = loginPage; } } //为了使core.security生效则须要一个@Configuration类 @Configuration @EnableConfigurationProperties(SecurityProperties.class) public class SecurityCoreConfig { } //最后要在权限配置类**BrowserSecurityConfig**中 配置放行的url private final static String loginPage = "/authentication/require"; @Autowired private SecurityProperties securityProperties; @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .loginPage(loginPage) .loginProcessingUrl("/authentication/form") .and() .authorizeRequests() .antMatchers(loginPage).permitAll() //自定义的登陆页面权限放开 .antMatchers(securityProperties.getBrowser().getLoginPage()).permitAll() .anyRequest().authenticated() .and() .csrf().disable(); }
第一种状况:访问请求:http://localhost:8080/user/1
第二种状况:访问请求:http://localhost:8080/index.html
第三种状况:关闭配置,继续访问请求:http://localhost:8080/index.html
#core.security.browser.loginPage = /demo-sign.html
这个功能就是咱们目前想要的,能够针对不一样的请求对于没有权限时的拦截以及调整判断。