Technology 4/10/2021
949
0
2

spring security 学习和踩坑总结(入门)

spring , security , form , java , 登录
spring security 学习和踩坑总结(入门)

Java Web项目的权限管理框架,目前有两个比较成熟且使用较多的框架,Shiro 和 Spring Security ,Shiro 比 Spring Security更加轻量级,但是需要手动配置的东西较多,Spring Security 和 Spring 集成更好,甚至直接适配了Spring Boot。

一、最简单的使用:

1.1、配置及使用

要使用Spring Security 首先要引入依赖,Spring-boot 已经有了集成,直接引入spring-boot-statr-security依赖包即可:

        <!-- spring security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

演示项目使用的是Spring boot 2.1.5版本,所以加入依赖后,会自动获取Spring Security 5.1.5版本的Jar包:

然后,就没有然后了,一个基本的Spring Security已经有了,然后打开浏览器,访问http://localhost:8080,神奇的出来了一个登录页面,但是并没有创建任何的html文件,resouces文件夹下除了两个配置文件,别的什么都没有了;

Spring Security已经默认做了一些配置,并且创建一个简单的登录页面,那这个页面事怎么来的?通过查看相关文档和源码来一探究竟。

那什么都没有配置,从哪开始开始看呢?

1.2 、分析原理

1.2.1、登录页面:

一般不知从何入手,就看官方文档里是如何做的,官方的文档和api 是最好最完整的介绍和参考,点击链接查看官方文档地址(https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc-oauth2login),或者通过Google 搜索 spring security,在结果中点击Spring Security Reference,点击进入页面,然后就可以看到关于Spring Security的文档;

通过查看文档发现,WebSecurityConfigurerAdapter 提供的默认的配置,那就来看看这个抽象类,在这个抽象类中,又一个方法配置了formLogin(),这个方法内容如下:

查看formLogin()源码,跳转到HttpSecurity类中,这个方法返回一个FormLoginConfigurer类型的数据。再继续来看看这个FormLoginConfigurer,在FormLoginConfigurer中有个initDefaultLoginFilter()方法:

这个方法,初始化一个默认登录页的过滤器,可以看到第一句代码,默认的过滤器是DefaultLoginPageGeneratingFilter,下面是设置一些必要的参数,进入到这个过滤器中:

在描述中可以看到,如果没有配置login页,这个过滤器会被创建,然后看doFilter()方法:

登录页面的配置是通过generateLoginPageHtml()方法创建的,再来看看这个方法内容:

private String generateLoginPageHtml(HttpServletRequest request, boolean loginError,
			boolean logoutSuccess) {
		String errorMsg = "none";
        // ...

		if (formLoginEnabled) {
			sb.append("<h3>Login with Username and Password</h3>");
			sb.append("<form name='f' action='").append(request.getContextPath())
					.append(authenticationUrl).append("' method='POST'>\n");
			sb.append("<table>\n");
			sb.append("	<tr><td>User:</td><td><input type='text' name='");
			sb.append(usernameParameter).append("' value='").append("'></td></tr>\n");
			sb.append("	<tr><td>Password:</td><td><input type='password' name='")
					.append(passwordParameter).append("'/></td></tr>\n");

			if (rememberMeParameter != null) {
				sb.append("	<tr><td><input type='checkbox' name='")
						.append(rememberMeParameter)
						.append("'/></td><td>Remember me on this computer.</td></tr>\n");
			}
			sb.append("	<tr><td colspan='2'><input name=\"submit\" type=\"submit\" value=\"Login\"/></td></tr>\n");
			renderHiddenInputs(sb, request);
			sb.append("</table>\n");
			sb.append("</form>");
		}

		// ...
	}

至此,默认登录页及配置,已经可以清楚了。

1.2.2、登录用户及密码:

在项目启动的日志中,可以发现有这样一条信息:

可以看到,自动配置类是UserDetailsServiceAutoConfiguration,密码是cf73184c-e8f2-48d8-9ce3-4413e3943f19,现在知道了密码,那用户名是什么还不知道,进入到UserDetailsServiceAutoConfiguration中去看看。

在这个 UserDetailsServiceAutoConfiguration 类的描述中可以知道,这个类是设置一些 Spring Security 相关默认的自动配置,把InMemoryUserDetailsManager 中得user 和 password 信息设置为默认得用户和密码,可以通过提供的AuthenticationManager、AuthenticationProvider 或者 UserDetailsService 的 bean 来覆盖默认的自动配置信息。

可以看到,日志中那句密码打印的是从图片中圈出来的这条语句打印的,在这个方法上面有一个inMemoryUserDetailsManager()方法,返回一个新的带有UserDetials信息参数构造的InMemoryUSerDetailsManager对象:

可以看到,第一个参数是User.withUsername(user.getName()),这个user.getName()的user 对象是上面SecurityProperties.User类型的,通过SecurityProperties 对象中获取的,首先看下SecurityProperties类:

通过配置文件中的,前缀为spring.security 的配置可以改变默认配置信息,再看看SecurityProperties 的 getUser()方法:

=>=>

通过一步步的跟踪,发现默认的用户名是user。现在需要定义一个成功之后返回信息的Controller,并写一个方法:

@Controller
public class IndexController {

    @RequestMapping("/")
    public String index() {
        return "/login";
    }
}

成功登录后,DispatcherServlet会将路由定位到“/”路径下,我们配置“/”再跳转到“/login”路径。

    @RequestMapping("/login")
    @ResponseBody
    public Boolean login() {
        return false;
    }

去默认的登录页面尝试一下,输入用户名:user ,密码:(每次启动都会重新生成一个UUID的字符串),登录成功了,跳转到了RequestMapping(“/”)然后又跳转至RequestMapping(“/login”),并返回结果,上面的提示信息说XML解析的问题,查了资料,在IE中是没有问题的,可以直接显示结果false,而谷歌显示XML类型的信息:

1.3、修改默认配置的登录页和请求处理接口:

修改默认的Security配置需要继承WebSecurityConfigurerAdapter类,然后覆写config(HttpSecurity http)方法;

1.3.1、修改登录页:

定制自己开发的登录页的,只需要调用formLogin().loginPage(“/customLogin”)即可。

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin().loginPage("/customLogin");
    }

注意:loginPage配置会经过模板解析即TemplateResolver处理,所以能写.ftl或者.html后缀。

1.3.1、修改登录请求:

默认的处理接口为login,如果想修改也是可以的,通过formLogin().loginProcessingUrl(“/singin”)可配置。

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin().loginProcessingUrl("/signin");
    }

这里配置的接口需要自己实现其逻辑。

Comments