社区网站7.2 权限控制

  登录检查
之前采用拦截器实现了登录检查,这是简单的权限管理方案,现在将其废弃。
  授权配置
对当前系统内包含的所有请求,分配访问权限(普通用户、版主、管理员)。
  认证方案
绕过Seccurity认证流程,采用系统原来的认证方案。
  CSRF配置
防止CSRF攻击的基本原理,以及表单、AJAX相关的配置。
  导入spring-boot-starter-security包到pom.xml。把WebMvcConfig类里LoginRequiredIntercepter相关配置注释掉。
  在CommunituConstant接口类里添加

/**
     * 系统用户ID
     */
    int SYSTEM_USER_ID = 1;

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

    /**
     * 权限:管理员
     */
    String AUTHORITY_ADMIN = "admin";

    /**
     * 权限:版主
     */
    String AUTHORITY_MODERATOR = "moderator";

  新建SecurityConfig类,

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter implements CommunityConstant {

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        super.configure(http);
        //授权
        //看看哪些controller需要做权限控制
        http.authorizeRequests()
                .antMatchers(
                       "/user/setting",
                       "/user/upload",
                       "/discuss/add",
                       "/comment/add/**",
                        "/letter/**",
                        "/notice/**",
                        "/like",
                        "/follow",
                        "/unfollow"
                )
                .hasAnyAuthority(
                        AUTHORITY_USER,
                        AUTHORITY_ADMIN,
                        AUTHORITY_MODERATOR
                )
                .antMatchers(
                     "/discuss/top",
                        "/discuss/wonderful"
                )
                .hasAnyAuthority(
                        AUTHORITY_MODERATOR
                )  //版主才有置顶、加精的权限(与上面那个antMatchers关联)
                .antMatchers(
                        "/discuss/delete",
                        "/data/**",
                        "/actuator/**"
                )
                .hasAnyAuthority(
                        AUTHORITY_ADMIN
                )
                .anyRequest().permitAll()
                .and().csrf().disable();

        //权限不够怎么处理
        http.exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    // 没有登录
                    @Override
                    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) 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");  //非异步请求,直接返回html页面
                        }
                    }
                })
                .accessDeniedHandler(new AccessDeniedHandler() {
                    // 权限不足
                    @Override
                    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) 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");  //非异步请求,直接返回html页面
                        }
                    }
                });
        //Security底层默认会拦截/logout,进行退出处理
        //覆盖它默认达到逻辑,才能执行我们自己的退出代码
        http.logout().logoutUrl("/securitylogout");
    }
}

  在UserService里,添加

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

        List<GrantedAuthority> list = new ArrayList<>();
        list.add(new GrantedAuthority() {  //这里权限只有一种,所以list只add一次
            @Override
            public String getAuthority() {
                switch (user.getType()){
                    case 1:
                        return AUTHORITY_ADMIN;
                    case 2:
                        return AUTHORITY_MODERATOR;
                    default:
                        return AUTHORITY_USER;
                }
            }
        });
        return list;
    }

  在LoginTicketInterceptor类preHandle方法里hostHolder.setUser(user);之后添加

//构建用户认证的结果,并存入SecurityContext,以便于Security进行授权,
                Authentication authentication = new UsernamePasswordAuthenticationToken(
                        user,user.getPassword(),userService.getAuthorities(user.getId()));
                SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
            

afterCompletion方法里hostHolder.clear();之后添加

        SecurityContextHolder.clearContext();

  在LoginController类logout方法里userService.logout(ticket);之后提添加

SecurityContextHolder.clearContext();

  关于CSRF攻击,spring security是用token来解决的。
CSRF攻击
  异步的时候没有表单,没法处理,那就要自己另外处理。
  例如发帖时,在index.html里头部添加

<meta name="_csrf" th:content="_csrf.token">
<meta name="_csrf_header" th:content="${_csrf.headerName}">

异步请求传数据不是通过请求体,是通过请求消息头,如上设置之后spring security会生成csrf的key和value。在index.js里添加

// 发送AJAX请求之前,将CSRF令牌设置达到请求的消息头中(其他很多页面需要挨个处理)
    var token = ${"meta[name='_csrf]"}.attr("content");
    var header = ${"meta[name='_csrf_header]"}.attr("content");
    $(document).ajaxSend(function(e,xhr,options){
        xhr.setRequestHeader(header,token);
    });

  但是这样设置的话,每个异步请求都要作相应处理,太麻烦了,因此注释掉csrf相关处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值