spring security spring boot

对spring security 做个小结:

 spring boot把spring mvc的配置文件改成了以代码的形式配置

WebSecurityConfig extends WebSecurityConfigurerAdapter

要继承这个类    这里面有很多的是父类的方法 重点看看这个方法:

protected void configure(HttpSecurity http) throws Exception {
        /**
         * 解决跨域
         * 1、csrf 禁用,ajax跨域请求会被拦截判定失效  csrf.disable()
         * 2、注册用户自定义的请求匹配器requireCsrfProtectionMatcher
         */
        http.exceptionHandling().accessDeniedPage("/authExp").and().authorizeRequests()
            .antMatchers("/message/*").permitAll().antMatchers("/error").permitAll()
            .antMatchers("/**").hasRole("USER").and().formLogin().loginPage("/logon").permitAll()
            .and().logout().invalidateHttpSession(true).logoutUrl("/logout").and().csrf()
            .requireCsrfProtectionMatcher(userRequiresCsrfMatcher()).and().rememberMe().key(remKey)
            .rememberMeServices(rememberMeServices()).and().sessionManagement()
            .sessionAuthenticationStrategy(sessionAuthenticationStrategy())
            .invalidSessionUrl("/logon").and()
            .addFilterAt(ajaxLoginFilter(), UsernamePasswordAuthenticationFilter.class)
            .addFilterAt(mySecurityFilter(), FilterSecurityInterceptor.class);
    }

配置的很多url 和mvc 里面xml配置是一样的道理  没有权限 就访问/authExp   配置不需要的权限访问 配置基本角色  默认加  “ROLE_” 以及一些filter

下面需要设置要被控制的url

 public Map<String, Collection<ConfigAttribute>> getAllResources() {
        Map<String, Collection<ConfigAttribute>> map = new HashMap<>();
        //获取url资源
        List<SysResources> listDB = sysResourcesDao.getUrlResources();
        for (SysResources sysResources : listDB) {
            Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
            //以权限名封装为Spring的security Object
            ConfigAttribute config = new SecurityConfig("ROLE_" + sysResources.getResourceId()
                                                        + "_" + sysResources.getResourceName());
            configAttributes.add(config);
            map.put(sysResources.getResourcePath(), configAttributes);
        }
        return map;
    }


和 用户自己的资源

@Override
    public Set<GrantedAuthority> getAuthorities(LogonInfo user) {
        //SET去重
        Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
        //这里要查数据库,由于在remember me时 要先调用此方法   然而当前登录用户还没有初始化权限信息而报错
        List<SysResources> listSysresourcesDB = sysResourcesDao.getUrlResourcesByUid(user.getuId());
        for (SysResources SysResources : listSysresourcesDB) {
            authSet.add(new SimpleGrantedAuthority("ROLE_" + SysResources.getResourceId() + "_"
                                                   + SysResources.getResourceName()));
        }
        //给所有登录用户自动增加ROLE_USER权限
        authSet.add(new SimpleGrantedAuthority("ROLE_USER"));
        return authSet;
    }

上面的 ROLE_USER一定要加

AjaxAuthenticationFailureHandler

@Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException,
                                                                          ServletException {
        NetworkUtil.responseJSONMsg(response, ResultInfo.createResult(CodeEnum.ERROR_10001));
    }

AjaxAuthenticationProvider

 @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response)
                                                                             throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: "
                                                     + request.getMethod());
        }


        String username = obtainUsername(request);
        String password = obtainPassword(request);


        if (username == null) {
            username = "";
        }


        if (password == null) {
            password = "";
        }
        logger.debug(">>>attemptAuthentication()-验证用户账号密码<<<");
        username = username.trim();
        //根据用户名到数据库取出用户数据
        LogonInfo user = springSecurityService.getByNameWithNoAuth(username);
        //验证登录用户信息
        if (user == null) {
            logger.error("用户不存在!");
            throw new AuthenticationServiceException("用户不存在!");
        }
        if (!user.getLoginPwd().equals(DigestPass.getDigestPassWord(password))) {
            logger.error("密码输入错误!");
            throw new AuthenticationServiceException("密码输入错误!");
        }
        if (user.getDisableFlag().equals(IGlobalConstant.DISABLED)) {
            logger.error("用户失效,无法登录!");
            throw new AuthenticationServiceException("用户失效,无法登录!");
        }


        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
            username, DigestPass.getDigestPassWord(password));


        //验证用户信息
        springSecurityService.validateUser(user);
        //登录之后具体操作,后期用service统一处理
        springSecurityService.initData(user);


        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);


        return this.getAuthenticationManager().authenticate(authRequest);
    }


AjaxAuthenticationSuccessHandler

 @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException,
                                                                      ServletException {
        NetworkUtil.responseJSONMsg(response, ResultInfo.createResult(CodeEnum.SUCCESS));
    }

MyAccessDecisionManager

 public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
                                                                    InsufficientAuthenticationException {
        logger.info(">>>decide()-判断用户是否具有访问该url权限<<<");
        if (configAttributes == null) {
            return;
        }
        //所请求的资源拥有的权限(一个资源对多个权限)
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            //访问所请求资源所需要的权限
            String needPermission = configAttribute.getAttribute();
            logger.info("needPermission is " + needPermission);
            //用户所拥有的权限authentication
            for (GrantedAuthority ga : authentication.getAuthorities()) {
                if (needPermission.equals(ga.getAuthority())) {
                    return;
                }
            }
        }
        logger.error(">>>decide()- 没有权限访问!<<<");
        //没有权限让我们去捕捉
        throw new AccessDeniedException(" 没有权限访问!");
    }

MySecurityFilter

MySecurityMetadataSource

 /**
     * 返回该次请求资源所需要的权限,与数据库中的url进行匹配,如果匹配成功则进行权限验证,因此这方法决定url是否需要权限验证
     * 即调用@see MyAccessDecisionManager#decide(org.springframework.security.core.Authentication, Object, Collection)
     * @param object
     * @return
     * @throws IllegalArgumentException
     * @see org.springframework.security.access.SecurityMetadataSource#getAttributes(java.lang.Object)
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        //从缓存取所有资源
        Map<String, Collection<ConfigAttribute>> resourceMap = ehCacheService.getAllResources();
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        logger.debug(">>>requestUrl is {} <<<", requestUrl);
        Collection<ConfigAttribute> string = null;
        // 检测请求与当前资源匹配的正确性  
        Iterator<String> iterator = resourceMap.keySet().iterator();
        while (iterator.hasNext()) {
            String uri = iterator.next();
            boolean flag = ehCacheService.getMatchResult(uri, requestUrl);
            logger.debug("{}  {}", uri, flag);
            if (flag) {
                string = resourceMap.get(uri);
                break;
            }
        }
        return string;
    }


MyTokenBasedRememberMeServices

 @Override
    protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
                                                 HttpServletResponse response) {


        if (cookieTokens.length != 3) {
            throw new InvalidCookieException("Cookie token did not contain 3"
                                             + " tokens, but contained '"
                                             + Arrays.asList(cookieTokens) + "'");
        }


        long tokenExpiryTime;


        try {
            tokenExpiryTime = new Long(cookieTokens[1]).longValue();
        } catch (NumberFormatException nfe) {
            throw new InvalidCookieException(
                "Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1]
                        + "')");
        }


        if (isTokenExpired(tokenExpiryTime)) {
            throw new InvalidCookieException("Cookie token[1] has expired (expired on '"
                                             + new Date(tokenExpiryTime) + "'; current time is '"
                                             + new Date() + "')");
        }
        String ipaddress = "";
        try {
            ipaddress = NetworkUtil.getIpAddress(request);
        } catch (IOException e) {
            logger.error("Error{}", e);
        }


        // Check the user exists.
        // Defer lookup until after expiry time checked, to possibly avoid expensive database call.


        UserDetails userDetails = getUserDetailsService().loadUserByUsername(cookieTokens[0]);


        // Check signature of token matches remaining details.
        // Must do this after user lookup, as we need the DAO-derived password.
        // If efficiency was a major issue, just add in a UserCache implementation,
        // but recall that this method is usually only called once per HttpSession - if the token is valid,
        // it will cause SecurityContextHolder population, whilst if invalid, will cause the cookie to be cancelled.
        String expectedTokenSignature = makeTokenSignature(tokenExpiryTime,
            userDetails.getUsername(), userDetails.getPassword(), ipaddress);


        if (!equals(expectedTokenSignature, cookieTokens[2])) {
            throw new InvalidCookieException("Cookie token[2] contained signature '"
                                             + cookieTokens[2] + "' but expected '"
                                             + expectedTokenSignature + "'");
        }
        LogonInfo users = springSecurityService.getByNameWithNoAuth(cookieTokens[0]);
        springSecurityService.initData(users);
        return userDetails;
    }


UserDetailServiceImpl


@Override
    public UserDetails loadUserByUsername(String loginName) throws UsernameNotFoundException {
        logger.debug(">>>loadUserByUsername()-获取用户信息<<<");
        //获取用户信息
        LogonInfo user = springSecurityService.getByNameWithNoAuth(loginName);
        if (user == null) {
            logger.debug(loginName + "用户不存在!");
            throw new UsernameNotFoundException(loginName + "用户不存在!");
        }
        //封装成spring security的user
        return new User(user.getLoginName(), user.getLoginPwd(), true, true, true, true,
            ehCacheService.getAuthorities(user));
    }


然后就是这几个类 第一个是登录失败进的、第二个是控制登录,初始化user数据到session的 第三个是登录成功过后进入、4权限判断  MySecurityMetadataSource(判断要进入权限验证的url,如果不匹配就不进  MyAccessDecisionManager 的 decide方法 ) 记住登录、封装成spring security的user


1.用户的URL被MySecurityFilter拦截,并交给MySecurityMetadataSource
2.在MySecurityMetadataSource中,将拦截到的url与数据库中所有激活的Resource的url进行match(有正则)
如果没有找到匹配成功的,则直接放行,不必经过步骤3。
如果有找到匹配项,返回Resource对应的重新封装的值如 "ROLE_" + sysResources.getResourceId()
                                                        + "_" + sysResources.getResourceName()",然后交给MyAccessDecisionManager
(PS:用户登录的时候,系统会查找用户对应的角色以及这些角色对应的资源,并将这些资源重新封装,
                即从用户->角色->资源整合成用户->资源这种模式,然后直接封装用户可以访问哪些资源,只保存封装后的资源名称如:"ROLE_" + sysResources.getResourceId()
                                                        + "_" + sysResources.getResourceName()")
3.MyAccessDecisionManager会在当前登录用户的重新封装后的权限列表里面查找资源名"ROLE_" + sysResources.getResourceId()
                                                        + "_" + sysResources.getResourceName()",
如找到,则放行。
如没有找到,则返回配置文件中配置的权限验证失败后的默认放回页面。

MySecurityFilter 在WebSecurityConfig这里定义的 .addFilterAt(mySecurityFilter(), FilterSecurityInterceptor.class);

还可以定义跨域拦截,在yml中配 指定域名访问


到此结束



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值