社区网站项目7.1 Spring Security

  简介
(1)Spring Security是一个专注于为Java应用程序提供身份认证和授权的框架,它的强大之处在于它可以轻松扩展以满足自定义的需求。
(2)官网在https://spring.io/projects/spring-security
(3)推荐资料:www.spring4all.com
  特征
(1)对身份的认证授权提供全面的、可扩展的支持。
(2)防止各种攻击,如会话固定攻击、点击劫持、crf攻击等。
(3)支持与Service API、Spring MVC等Web技术集成。
  原理示意图(spring-security是基于filter)
spring security
  在mvn库里搜索spring security,把spring-boot-starter-security复制到pom.xml。在User类里添加

// true:账号未过期(在此不做过期处理)
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

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

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

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

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

  在UserService类里,实现UserDetailService接口,并添加

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

  新建SecurityConfig类

@Override
    public void configure(WebSecurity web) throws Exception {
//        super.configure(web);
        //忽略静态资源的访问
        web.ignoring().antMatchers("/resources/**"); // /resources/**主要是图片、js、css等静态资源,不需要拦截
    }

    // AuthenticationManager:认证的核心接口
    // AuthenticationManagerBuilder:用于构建AuthenticationManager对象的工具
    // ProviderManager:AuthenticationManager接口默认实现类
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        super.configure(auth);
        // 内置的认证规则
//        auth.userDetailsService(userService).passwordEncoder(new Pbkdf2PasswordEncoder("12345")); //后面这个字符串是加的盐但这个盐和我们系统原来的盐不匹配
        // 自定义认证规则
        // AuthenticationProvider:ProviderManager(一个集合)持有一组AuthenticationProvider,每个AuthenticationProvider负责一种认证
        // 委托模式:ProviderManager将认证委托给AuthenticationProvider。
        auth.authenticationProvider(new AuthenticationProvider() {
            // Authentication:用于封装认证信息的接口,不同的实现类代表不同类型的认证信息(如:账号密码/qq/微信/人脸/指纹信息,这里用的是【账号密码】)。
            @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 {
//        super.configure(http); //避开父类默认的登录页面
        // 登录相关配置
        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()
                .logoutUrl("/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") //只有"ADMIN"才有权限访问"/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;
                    }
                }
                //让请求继续向下执行,如果后面还有多个filter的话
                filterChain.doFilter(request,response);
            }
        }, UsernamePasswordAuthenticationFilter.class);

        // 记住我,存到内存里,若要存到redis的话另外实现
        http.rememberMe()
                .tokenRepository(new InMemoryTokenRepositoryImpl())
                .tokenValiditySeconds(3600*24)
                .userDetailsService(userService); //查出用户信息自动帮你过

    }`


  在HomeController类里,添加

@RequestMapping(path = "/index", method = RequestMethod.GET)
    public String getIndexPage(Model model) {
        // 认证成功后,结果会通过SecurityContextHolder存入SecurityContext中
        Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if(obj instanceof User){
            model.addAttribute("loginUser",obj);
        }
        return "/index";
    }
    
// 拒绝访问时的提示页面(或者说没有权限时)
    @RequestMapping(path = "/denied", method = RequestMethod.GET)
    public String getDeniedPage(){
        return "/error/404";
    }

  对login.html和index.html作相应修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值