我们先看spring secuity
的默认登录页面,
- 加入
springmvc
,spring secuity
,servlet
的一些依赖,配置jetty的插件,配置端口是8001,contextPath
是"/"
org.springframework
spring-webmvc
4.3.13.RELEASE
org.springframework.security
spring-security-web
4.2.3.RELEASE
org.springframework.security
spring-security-config
4.2.3.RELEASE
javax.servlet
javax.servlet-api
3.1.0
provided
javax.servlet.jsp
jsp-api
2.2
provided
secuity-quickstart-config
org.apache.maven.plugins
maven-war-plugin
3.0.0
false
org.eclipse.jetty
jetty-maven-plugin
9.4.3.v20170317
8001
/
- 定义系统启动类
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//系统启动的时候的根类
@Override
protected Class>[] getRootConfigClasses() {
return new Class>[]{WebAppConfig.class};
}
@Override
protected Class>[] getServletConfigClasses() {
return null;
}
//设置成/*表示拦截静态的文件
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
- web入口类
/**
*
* 入口类,启动spring mvc,启动spring secuity
*/
@EnableWebMvc
@EnableWebSecurity
@ComponentScan("com.zhihao.miao.secuity")
public class WebAppConfig extends WebMvcConfigurerAdapter {
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
- spring security配置类
/**
*
* 初始化spring security
*/
public class WebAppSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
protected String getDispatcherWebApplicationContextSuffix() {
return AbstractDispatcherServletInitializer.DEFAULT_SERVLET_NAME;
}
}
- 具体的controller
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello spring secuity";
}
@GetMapping("/home")
public String home(){
return "home spring security";
}
@GetMapping("/admin")
public String admin(){
return "admin spring secuity";
}
}
- 权限用户名密码的具体配置
Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("GUEST");
auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").roles("USER");
auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/hello").hasRole("GUEST");
http.authorizeRequests().antMatchers("/home").hasRole("USER");
http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");
http.authorizeRequests().antMatchers("/**/*.html").permitAll();
http.authorizeRequests().antMatchers("/**/*.css").permitAll();
http.authorizeRequests().antMatchers("/**/*.js").permitAll();
http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");
http.authorizeRequests().anyRequest().authenticated();
//httpbasee认证
http.httpBasic();
}
}
-
默认的登录页面
http.formLogin();是spring secuity默认的登录页面。
自定义登录
- 先定义一个登录页面,将其页面放在了
WEB-INF
下面的jsp
目录下,然后需要在启动类上加入视图解析器
@EnableWebMvc
@EnableWebSecurity
@ComponentScan("com.zhihao.miao.secuity")
public class WebAppConfig extends WebMvcConfigurerAdapter {
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//配置视图解析器
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp();
}
}
-
Controller
中定义一个url跳转到该登录页面
根据上面的视图解析器,我们就知道登录的跳转页面的路径是/WEB-INF/jsp/login.jsp
@Controller
public class LoginController {
@GetMapping("/sys/login")
public String login(){
return "/jsp/login";
}
}
- 在
spring security
中配置
登录的跳转页面,和登录的动作url不去做权限认证。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("GUEST");
auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").roles("USER");
auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/hello").hasRole("GUEST");
http.authorizeRequests().antMatchers("/home").hasRole("USER");
http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");
//登录的跳转页面,和登录的动作url不应该有权限认证。
http.authorizeRequests().antMatchers("/sys/login").permitAll();
http.authorizeRequests().antMatchers("/**/*.html").permitAll();
http.authorizeRequests().antMatchers("/**/*.css").permitAll();
http.authorizeRequests().antMatchers("/**/*.js").permitAll();
http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");
http.authorizeRequests().anyRequest().authenticated();
http.formLogin().
//登录的时候跳转的登录页面url
loginPage("/sys/login").
//登录页面提交时候的请求
loginProcessingUrl("/doLogin").
defaultSuccessUrl("/public/login/ok.html"). //如果直接访问登录页面,则登录成功后重定向到这个页面,否则跳转到之前想要访问的页面
permitAll(); //就是设置loginProcessingUrl()也不需要权限认证
}
}
- 登录页面:
详细的登录页面可以查看文章的最后的项目链接
Login
- 测试
访问localhost:8001/hello
,跳转到http://localhost:8001/sys/login
页面,具体页面如下:
- 一些更加细节的定制登录的api使用
比如说失败重定向(可以在重定向方法中获取到失败的异常),失败跳转,成功登录之后重定向等等api的使用
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//账号被锁
auth.inMemoryAuthentication().withUser("zhangsan").password("123456").accountLocked(true).roles("GUEST");
//账号过期
auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").accountExpired(true).roles("USER");
auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/hello").hasRole("GUEST");
http.authorizeRequests().antMatchers("/home").hasRole("USER");
http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");
//登录的跳转页面,和登录的动作url不应该有权限认证。
http.authorizeRequests().antMatchers("/sys/login").permitAll();
http.authorizeRequests().antMatchers("/**/*.html").permitAll();
http.authorizeRequests().antMatchers("/**/*.css").permitAll();
http.authorizeRequests().antMatchers("/**/*.js").permitAll();
http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");
http.authorizeRequests().anyRequest().authenticated();
http.formLogin().
loginPage("/sys/login").
loginProcessingUrl("/doLogin").
failureForwardUrl("/sys/loginFail"). //使用forward的方式,能拿到具体失败的原因,并且会将错误信息以SPRING_SECURITY_LAST_EXCEPTION的key的形式将AuthenticationException对象保存到request域中
//failureUrl("/public/login/fail.html"). //失败重定向,拿不到具体失败的原因
defaultSuccessUrl("/public/login/ok.html"). //如果直接访问登录页面,则登录成功后重定向到这个页面,否则跳转到之前想要访问的页面
//defaultSuccessUrl("/public/login/ok.html",true). //登录成功后,都直接重定向到这个页面
permitAll();
}
}
比如说重定向拿不到登录失败的异常,而failureForwardUrl()
的api却可以,点入failureForwardUrl
源码查看,FormLoginConfigurer
的文档说明,如果登录失败会抛出
SPRING_SECURITY_LAST_EXCEPTION
异常,取到消息可以使用${SPRING_SECURITY_LAST_EXCEPTION.message}
,
可以在Controller
层中通过HttpServletRequest
拿到登录失败的异常,
@PostMapping("/sys/loginFail")
public String fail(HttpServletRequest req){
AuthenticationException exp = (AuthenticationException)req.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
System.out.println("exp:"+exp.getMessage());
if(exp instanceof BadCredentialsException){
//将错误信息放到request域中
req.setAttribute("error_msg", "用户名或密码错误");
} else if(exp instanceof AccountExpiredException){
req.setAttribute("error_msg", "账户过期");
} else if(exp instanceof LockedException){
req.setAttribute("error_msg", "账户已被锁");
}else{
//其他错误打印这些信息
System.out.println(exp.getMessage());
}
return "/jsp/login";
}
登录页面打印失败的异常
Login
${SPRING_SECURITY_LAST_EXCEPTION.message}
此时就可以把错误信息打印到页面上
- 还可以自定义登录成功和失败的handler进行权限验证,自己根据自己的业务代码来进行定制
通过successHandler
和failureHandler
方法来定义
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("GUEST");
auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").roles("USER");
auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/hello").hasRole("GUEST");
http.authorizeRequests().antMatchers("/home").hasRole("USER");
http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");
//登录的跳转页面,和登录的动作url不应该有权限认证。
http.authorizeRequests().antMatchers("/sys/login").permitAll();
http.authorizeRequests().antMatchers("/**/*.html").permitAll();
http.authorizeRequests().antMatchers("/**/*.css").permitAll();
http.authorizeRequests().antMatchers("/**/*.js").permitAll();
http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");
http.authorizeRequests().anyRequest().authenticated();
http.formLogin().
loginPage("/sys/login").
loginProcessingUrl("/doLogin").
successHandler((request, response, authentication) -> {
//登录成功的时候跳转到/public/login/ok.html
System.out.println("========登陆成功=======" + authentication.getName());
response.sendRedirect("/public/login/ok.html");
}).failureHandler((request, response, exception) -> {
//登录失败的时候跳转到/public/login/fail.html
System.out.println("=======登陆失败=======" + exception.getMessage());
response.sendRedirect("/public/login/fail.html");
}).permitAll();
}
}
参考代码
secuity-config-login
参考资料
官方文档
Spring Security 从入门到进阶系列教程