[牛客社区开发(六)]SpringSecurity

[牛客社区开发(六)]SpringSecurity

一、基础配置

[牛客社区开发(六)]SpringSecurity_第1张图片

[牛客社区开发(六)]SpringSecurity_第2张图片

1.导入依赖

<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-securityartifactId>
dependency>

2.改写User实体类

让User实体继承UserDetails接口,实现重写方法。

public class User implements UserDetails {
    //true:账号未过期
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    //true:账号未锁定
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    //true:凭证未1过期
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    //true:账号可用
    @Override
    public boolean isEnabled() {
        return true;
    }


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        //一个GrantedAuthority代表一个权限
        List<GrantedAuthority> list = new ArrayList<>();
        list.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                switch (type){
                    case 1:
                        return "ADMIN";
                    default:
                        return "USER";
                }
            }
        });
        return list;
    }

3.编写UserService类

让UserService类继承UserDetailsService接口,重写loadUserByUsername方法

Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    public User findUserByName(String username) {

        return userMapper.selectByName(username);

    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return this.findUserByName(username);
    }
}

4.编写配置类SecurityConfig

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    public void configure(WebSecurity web) throws Exception {
        //忽略静态资源的访问提高性能
        web.ignoring().antMatchers("/resources/**");
    }

    // AuthorizationManager:认证的核心接口
    // AuthorizationManagerBuilder:用于构建AuthenticationManager对象的工具
    // ProvideManager:AuthenticationManager接口的默认实现类。
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //内置的认证规则
        //new Pbkdf2PasswordEncoder("12345") 内置加密工具
        //auth.userDetailsService(userService).passwordEncoder(new Pbkdf2PasswordEncoder("12345"));

        //自定义认证规则
        //AuthenticationProvider:ProviderManager持有一组AuthenticationProvider,每个AuthenticationProvider负责一种认证。
        // 委托模式:ProviderManager将认证委托给AuthenticationProvider
        auth.authenticationProvider(new AuthenticationProvider() {
            //Authentication:用于封装认证信息的接口,宝塔的实现类代表不同类型的认证信息
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                String username = authentication.getName();
                String password = (String) authentication.getCredentials();

                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支持那种类型的认证
            @Override
            public boolean supports(Class<?> aClass) {
                //UsernamePasswordAuthenticationToken:Authentication接口的长哟的实现类
                return UsernamePasswordAuthenticationToken.class.equals(aClass);
            }
        });
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //登录相关配置
        http.formLogin()
                .loginPage("/loginpage")
                .loginProcessingUrl("/login")
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.sendRedirect(request.getContextPath() + "/index");

                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                        request.setAttribute("error",e.getMessage());
                        request.getRequestDispatcher("/loginpage").forward(request,response);
                    }
                });

        //退出相关配置
        http.logout()
                .logoutSuccessUrl("/logout")
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.sendRedirect(request.getContextPath() + "/index");
                    }
                });

        //授权配置
        http.authorizeRequests()
                .antMatchers("/letter").hasAnyAuthority("USER","ADMIN")
                .antMatchers("/admin").hasAnyAuthority("ADMIN")
                .and().exceptionHandling().accessDeniedPage("/denied");

        //增加Filter,处理验证码
        http.addFilterBefore(new Filter() {
            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                HttpServletRequest request = (HttpServletRequest)servletRequest;
                HttpServletResponse response = (HttpServletResponse)servletResponse;
                if (request.getServletPath().equals("/login")){
                    String verifyCode = request.getParameter("verifyCode");
                    if (verifyCode == null || !verifyCode.equalsIgnoreCase("1234")){
                        request.setAttribute("error","验证码错误!");
                        request.getRequestDispatcher("/loginpage").forward(request,response);
                        return;
                    }
                }
                //让请求继续向下走,让请求放行
                filterChain.doFilter(request,response);
            }
        }, UsernamePasswordAuthenticationFilter.class);

        //记住我
        http.rememberMe()
                .tokenRepository(new InMemoryTokenRepositoryImpl())
                .tokenValiditySeconds(3600 * 24)
                .userDetailsService(userService);
    }
}

二、整合到牛客网

1.添加常量CommunityConstant

    /**
     * 权限:普通用户
     */
    String AUTHORITY_USER = "user";

    /**
     * 权限:管理员
     */
    String AUTHORITY_MODERATOR = "moderator";

2.SecurityConfig继承WebSecurityConfigurerAdapter

重写

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter implements CommunityConstant {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //授权
        http.authorizeRequests()
                .antMatchers(
                        "/user/setting",
                        "/user/upload",
                        "/discuss/add",
                        "/comment/add/**",
                        "/letter/**",
                        "notice/**",
                        "/like",
                        "/follow",
                        "/unfollow"
                )
                .hasAnyAuthority(
                        AUTHORITY_USER,
                        AUTHORITY_ADMIN,
                        AUTHORITY_MODERATOR
                )
                .anyRequest().permitAll()
                .and().csrf().disable() ;

        //权限不够的时候处理
        http.exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    //没有登录
                    @Override
                    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                        String xRequestedWith = request.getHeader("x-requested-with");
                        if ("XMLHttpRequest".equals(xRequestedWith)){
                            response.setContentType("application/plain;charset=utf-8");
                            PrintWriter writer = response.getWriter();
                            writer.write(CommunityUtil.getJSONString(403,"你还没有登录哦!"));
                        }else {
                            response.sendRedirect(request.getContextPath() + "/login");
                        }
                    }
                })
                .accessDeniedHandler(new AccessDeniedHandler() {
                    //权限不够
                    @Override
                    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
                        String xRequestedWith = request.getHeader("x-requested-with");
                        if ("XMLHttpRequest".equals(xRequestedWith)){
                            response.setContentType("application/plain;charset=utf-8");
                            PrintWriter writer = response.getWriter();
                            writer.write(CommunityUtil.getJSONString(403,"你没有访问此功能的权限!!"));
                        }else {
                            response.sendRedirect(request.getContextPath() + "/ denied");
                        }
                    }
                });
        //security底层默认会拦截/logout请求,进行退出处理。
        //覆盖它默认的逻辑,才能执行我们自己的退出代码
        //security首先是将用户token和权限存入securityContext里,登录的时候从Context中获取相关的权限
        http.logout().logoutUrl("/securitylogout");
    }
}

3.在UserService中添加方法getAuthorities

    public Collection<? extends GrantedAuthority> getAuthorities(int userId){
        User user = this.findUserById(userId);

        List<GrantedAuthority> list = new ArrayList<>();
        list.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                switch (user.getType()) {
                    case 1:
                        return AUTHORITY_ADMIN;
                    case 2:
                        return AUTHORITY_MODERATOR;
                    default:
                        return AUTHORITY_USER;
                }
            }
        });
        return list;
    }

4.修改拦截器代码

在LoginTicketInterceptor中的preHandle和afterHandle中添加代码

[牛客社区开发(六)]SpringSecurity_第3张图片

image-20220414134906149

5.CSRF攻击(了解)

[牛客社区开发(六)]SpringSecurity_第4张图片

当你浏览器访问网站A的时候,cookie和ticket存在浏览器,然后你再访问了有病毒的X网站。则X网站则会窃取你浏览器中的cookie和ticket发送请求给服务器进行一些不正当的操作。这种CRSF攻击发生在提交表单的时候。

解决方法就是,提交表单给服务器后服务器返回数据给浏览器的时候,带一个tocken一起返回。此时有病毒的网站只能获取你的cookie和ticket但不能获取你的tocken。

[牛客社区开发(六)]SpringSecurity_第5张图片

你可能感兴趣的:(java,spring,springboot)