在一个系统中认证和授权是常有的事情,现在比较流行的框架有spring security、shiro等等。他们都能很好的帮助我们完成认证和授权的功能。那么假如说让我们自己完成一个登录那么应该大致的流程是怎么样的呢?
一、我们肯定有一个处理页面上表单提交的url处理器,此处我们放置在Filter中进行处理,假如就叫做UsernamePasswordAuthenticationFilter ,在这个Filter里面我们需要将用户提交上来的用户名和密码封装成一个对象,假如叫做UsernamePasswordAuthenticationToken。
二、有了上一步的 UsernamePasswordAuthenticationToken 之后,我们肯定要对这个token进行一个认证,那UsernamePasswordAuthenticationFilter中就必须要有一个认证管理器(AuthenticationManager),来处理我们这个token的认证。
三、有了认证管理器后,那这个认证管理器可能可以处理多种方式的登录,比如基于ip地址的登录、基于用户名和密码的登录、基于remember me(记住我)的登录等等。那么在我们的认证管理器的实现中(ProviderManager)就应该存在一组AuthenticationProvider,每个AuthenticationProvider处理不同的Token认证。
比如:DaoAuthenticationProvider用于处理UsernamePasswordAuthenticationToken
RememberMeAuthenticationProvider用于处理RememberMeAuthenticationToken
四、当我们是基于用户名和密码登录认证时,那么对应的AuthenticationProvider就是DaoAuthenticationProvider, 那么我们肯定有一个接口根据用户输入的用户名返回一个用户对象即 UserDetailsService#loadUserByUsername(String) 接口,返回一个UserDetails 对象,有了用户对象后,我们系统中密码肯定是加密过的,那么就必须还要一个密码加密器PasswordEncoder,用于进行密码的校验。
五、当完成上面的认证后
成功认证:那么肯定有一个成功后的回调,加入是 AuthenticationFailureHandler , 在这个里面我们可以完成成功后页面的跳转
认证失败:也有一个失败的回调,假如是 AuthenticationFailureHandler,可以记录失败的日志,完成失败后的跳转
有了上面的初步认识后,我们来看一下在spring security中是如何做的。
一、引入spring security的pom配置文件
<dependencyManagement> <dependencies> <dependency> <groupId>io.spring.platform</groupId> <artifactId>platform-bom</artifactId> <version>Brussels-SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
二、进行spring security的配置
/** * spring security 的配置 * * @描述 * @作者 huan * @时间 2017年11月1日 - 下午9:33:59 */ @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(createUserDetailService())// .passwordEncoder(passwordEncoder())// .and()// .inMemoryAuthentication()// .withUser("admin") .password("admin") .roles("ADMIN");// 配置一个拥有用户名和密码的用户 } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() // 表单登录 .loginProcessingUrl("/login")// 处理登录的请求 .loginPage("/login.html") // 自定义登录界面 .usernameParameter("authUsername")//登录表单的用户名的name的值 .passwordParameter("authPassword")//登录表单密码的name的值 .successHandler(securityAuthenticationSuccessHandler())// 认证成功后的处理 .failureHandler(securityAuthenticationFailureHandler()) // 认证失败后的处理 .and()// .userDetailsService(createUserDetailService()) // 用户服务,根据用户名加载用户信息 .logout() // 登出 .logoutUrl("/logout") // 登出的url路径 .addLogoutHandler(securityLogoutHandler())//登出时的处理操作 .clearAuthentication(true)// 清除认证 .invalidateHttpSession(true)// session失效 .and()// .csrf() // csrf .disable()// .authorizeRequests()// .antMatchers("/login.html","/login").permitAll() // 可匿名访问 .anyRequest().authenticated() // 除了上方的请求,其余的请求需要认证才可以访问 .and()// .exceptionHandling()// .accessDeniedPage("/403.html"); // 没有权限访问的页面 } @Bean public AuthenticationFailureHandler securityAuthenticationFailureHandler() { SecurityAuthenticationFailureHandler authenticationFailureHandler = new SecurityAuthenticationFailureHandler(); authenticationFailureHandler.setDefaultFailureUrl("/login.html?error"); return authenticationFailureHandler; } @Bean public AuthenticationSuccessHandler securityAuthenticationSuccessHandler() { SecurityAuthenticationSuccessHandler authenticationSuccessHandler = new SecurityAuthenticationSuccessHandler(); authenticationSuccessHandler.setAlwaysUseDefaultTargetUrl(true); authenticationSuccessHandler.setDefaultTargetUrl("/index.html"); return authenticationSuccessHandler; } @Bean public LogoutHandler securityLogoutHandler() { return new SecurityLogoutHandler(); } @Bean public UserDetailsService createUserDetailService() { return new SecurityUserDetailServiceImpl(passwordEncoder()); } /** 密码加密器 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
三、各个实体类
四、登录页
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>用户登录</title> </head> <body> <form action="/login" method="post"> 用户名:<input type="text" name="authUsername" /><br /> 密码:<input type="password" name="authPassword" /><br/> <input type="submit" value="登录"> </form> </body> </html>
五、首页
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>首页</title> <link rel="stylesheet" type="text/css" href="/static/css/index.css"> </head> <body> <h1> 登录成功。<a href="/logout">退出</a> </h1> </body> </html>
六、界面效果
登录页
登录成功后的首页