👍SpringSecurity单体项目最佳实践( 二 )

  • 完成到这里,对于用户信息的功能已经实现 , 但是我们还没有配置我们的登陆界面 。
配置Security
  • ? 在config目录下创建SecurityConfig
@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter { @Resourceprivate UserService userService;}
  • ? 正常项目中,肯定会有许多的静态资源,这些都可以在不登录的情况下访问,如css、js等
@Overridepublic void configure(WebSecurity web) throws Exception {// 忽略静态资源web.ignoring().antMatchers("/resources/**");}
  • ? 当然我们上面的UserService只实现了认证的查询,并没有配置在何时去调用这个类 。
认证规则二选其一即可
// AuthenticationManager: 认证的核心接口// AuthenticationManagerBuilder: 用户构建AuthenticationManager对象的工厂类// ProviderManager: AuthenticationManager默认使用的实现类@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//内置的认证规则//auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());// 自定义认证规则// AuthenticationProvider: ProviderManager持有一组AuthenticationProvider,每个AuthenticationProvider负责一种认证// 委托模式:// AuthenticationProvider: 就好比登陆方式,不仅有密码登录 , 且还有微信,等其他登陆方式,每一种登陆方式对应一个AuthenticationProviderauth.authenticationProvider(new AuthenticationProvider() {// Authentication: 用于封装认证信息的接口 , 不同实现类代表不同类型的认证信息@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {String username = authentication.getName();String password = authentication.getCredentials().toString();User user = userService.findUserByName(username);if (user == null) {throw new UsernameNotFoundException("账号或密码错误!");}password = CommunityUtil.md5(password + user.getSalt());if (!user.getPassword().equals(password)) {throw new BadCredentialsException("账号或密码错误!");}// principal:认证的主要信息 credentials:代表用户 authorities:权限信息return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());}// 当前的AuthenticationProvider 支持哪种类型的认证 。@Overridepublic boolean supports(Class<?> aClass) {// UsernamePasswordAuthenticationToken: Authentication接口常用的实现类// 这样配置,我们当前项目只支持UsernamePasswordAuthenticationToken的认证return UsernamePasswordAuthenticationToken.class.equals(aClass);}});}
  • ? 配置了以上步骤,是不是觉得Security挺麻烦的,别急马上到头了 。
@Overrideprotected void configure(HttpSecurity http) throws Exception {// 登陆相关配置http.formLogin().loginPage("/loginpage") // 登陆页面.loginProcessingUrl("/login") // 处理登陆请求的路径.successHandler(new AuthenticationSuccessHandler() { // 认证成功处理器@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {// 重定向到主页面response.sendRedirect(request.getContextPath() + "/index");}}).failureHandler(new AuthenticationFailureHandler() { // 认证失败处理器@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {// 请求转发到登陆页面// 因为在项目中,登陆失败后,往往需要携带错误信息到页面展示,所有采用请求转发的方式request.setAttribute("error", e.getMessage());request.getRequestDispatcher("/loginpage").forward(request, response);}});// 退出相关配置http.logout().logoutUrl("/logout").logoutSuccessUrl("/index"); // 退出后重定向到的接口// 授权配置 配置什么路径只能什么权限访问http.authorizeRequests().antMatchers("/letter").hasAnyAuthority("USER", "ADMIN").antMatchers("/admin").hasAnyAuthority("ADMIN").and().exceptionHandling().accessDeniedPage("/denied"); //无权限时,重回定向到的页面}
  • 现在对于后端的配置就完成啦,前端界面建议直接从完成的项目中copy
  • ? 一定要检查数据库有没有对应的用户数据哦?。。?/li>
  • 接下来就是你们的时间啦 。自行测试
  • 但是正常的项目中,登陆功能一定会有验证码的存在,SpringSecurity也想到了这一点,我们都知道SpringSecurity是由一大串过滤器来完成对应功能的,也就是说,我们需要在登陆校验之前完成对于验证码的校验 。如下:
// 增加Filter 处理验证码http.addFilterBefore(new Filter() {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;if (request.getServletPath().equals("/login")) {// 正常项目中 验证码会存储在Session或者Redis中,为了方便 。此项目的验证码都是1234String verifyCode = request.getParameter("verifyCode");if (!"1234".equals(verifyCode)) {request.setAttribute("error", "验证码错误!");request.getRequestDispatcher("/loginpage").forward(request, response);return;}}// 放行请求,执行到下一个过滤器filterChain.doFilter(request, response);}}, UsernamePasswordAuthenticationFilter.class);// 记住我功能http.rememberMe().tokenRepository(new InMemoryTokenRepositoryImpl()) // 用户的令牌存储到哪,InMemoryTokenRepositoryImpl 存储到内存中.tokenValiditySeconds(3600 * 24) // 过期时间.userDetailsService(userService);// 当关闭浏览器后,第二次访问,去拿重新查询用户的数据

推荐阅读