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

  • ? 定义了UserDetails之后,当然还远远不够,哪个方法查询数据库来获取我们的用户信息呢?就是Security中的UserDetailsService接口

👍SpringSecurity单体项目最佳实践

文章插图
  • ? 它肯定也有默认实现类的,但是我们需要查询数据库对应的用户数据,所以我们还是采用自定义的方式去完成 。
@Servicepublic class UserService implements UserDetailsService {@Resourceprivate UserMapper userMapper;// 根据用户名去查询用户数据public User findUserByName(String username) {return userMapper.selectByName(username);}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return this.findUserByName(username);}}
  • 完成到这里,对于用户信息的功能已经实现,但是我们还没有配置我们的登陆界面 。
配置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只实现了认证的查询,并没有配置在何时去调用这个类 。
【👍SpringSecurity单体项目最佳实践】认证规则二选其一即可
// 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"); //无权限时,重回定向到的页面}

经验总结扩展阅读